Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37338776
en ru br
Репозитории ALT
S:3.4-alt2.qa2
5.1: 3.4-alt2
4.1: 3.4-alt1
4.0: 3.4-ipl11mdk.1
3.0: 3.4-ipl11mdk.1
www.altlinux.org/Changes

Группа :: Интерпретаторы команд
Пакет: sash

 Главная   Изменения   Спек   Патчи   Sources   Загрузить   Gear   Bugs and FR  Repocop 

Патч: sash-3.4-alt.patch
Скачать


--- sash/Makefile
+++ sash/Makefile
@@ -3,29 +3,36 @@
 #
 # The HAVE_GZIP definition adds the -gzip and -gunzip commands.
 # The HAVE_EXT2 definition adds the -chattr and -lsattr comamnds.
+# The WITH_READLINE definition adds readline support.
 #
 
-CFLAGS = -O3 -Wall -Wmissing-prototypes -DHAVE_GZIP -DHAVE_EXT2
-LDFLAGS = -static -s
-LIBS = -lz
+MKDIR = mkdir -p
+INSTALL = install -p
 
+CFLAGS = $(RPM_OPT_FLAGS) -D_GNU_SOURCE -DHAVE_GZIP -DHAVE_EXT2 -DWITH_READLINE
+LDFLAGS = -static
+LIBS = -lz -lreadline -ltinfo
 
-BINDIR = /bin
-MANDIR = /usr/man/man1
+sbindir = /sbin
+mandir = /usr/share/man
+mansubdir = man1
 
+OBJS = sash.o message.o getline.o utils.o \
+	cmds.o cmd_dd.o cmd_ed.o cmd_grep.o cmd_ls.o cmd_tar.o \
+	cmd_gzip.o cmd_find.o cmd_file.o cmd_chattr.o cmd_ar.o
 
-OBJS = sash.o cmds.o cmd_dd.o cmd_ed.o cmd_grep.o cmd_ls.o cmd_tar.o \
-	cmd_gzip.o cmd_find.o cmd_file.o cmd_chattr.o cmd_ar.o utils.o
+TARGET = sash
 
+$(TARGET):	$(OBJS)
+	$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
 
-sash:	$(OBJS)
-	$(CC) $(LDFLAGS) -o sash $(OBJS) $(LIBS)
+install: $(TARGET)
+	$(MKDIR) $(sbindir)
+	$(INSTALL) sash $(sbindir)/sash
+	$(MKDIR) $(mandir)/$(mansubdir)
+	$(INSTALL) -m644 sash.1 $(mandir)/$(mansubdir)/sash.1
 
 clean:
-	rm -f $(OBJS) sash
-
-install: sash
-	cp sash $(BINDIR)/sash
-	cp sash.1 $(MANDIR)/sash.1
+	rm -f $(OBJS) $(TARGET)
 
 $(OBJS):	sash.h
--- sash/cmd_ar.c
+++ sash/cmd_ar.c
@@ -16,14 +16,9 @@
 #include <ar.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
 #include <limits.h>
 
+#include "message.h"
 #include "sash.h"
 
 
@@ -70,14 +65,14 @@ static	int	shortNameMatches44BSD(const char * name);
 static	int	shortNameMatchesSysV(const char * name);
 
 static	BOOL	wantMember(const Archive * arch, int n_names,
-			const char ** names);
+			const char *const *names);
 
 static BOOL	getNumber(const char * s, unsigned base, int max_digits,
 			unsigned long * ul);
 
 
 void
-do_ar(int argc, const char ** argv)
+do_ar(int argc, const char *const *argv)
 {
 	const char *	options;
 	const char *	archiveName;
@@ -94,8 +89,7 @@ do_ar(int argc, const char ** argv)
 
 	if (argc < 3)
 	{
-		fprintf(stderr, "Too few arguments for ar\n");
-
+		errmsg( "ar: too few arguments." );
 		return;
 	}
 
@@ -135,22 +129,18 @@ do_ar(int argc, const char ** argv)
 			break;
 
 		case 'd': case 'm': case 'q': case 'r':
-			fprintf(stderr, "Writing ar files is not supported\n");
-
+			errmsg( "writing ar files is not supported." );
 			return;
 
 		default:
-			fprintf(stderr, "Unknown ar flag: %c\n", *options);
-
+			errmsg( "ar: unknown flag: %c", *options );
 			return;
 		}
 	}
 
 	if (doExtract + doTable + doPrint != 1)
 	{
-		fprintf(stderr,
-			"Exactly one of 'x', 'p' or 't' must be specified\n");
-
+		errmsg( "ar: exactly one of 'x', 'p' or 't' must be specified." );
 		return;
 	}
 
@@ -230,9 +220,7 @@ do_ar(int argc, const char ** argv)
 
 			if (close(outfd) == -1)
 			{
-				fprintf(stderr, "Can't close %s: %s\n",
-					arch.name, strerror(errno));
-
+				errmsg( "ar: failed to close %s: %s", arch.name, strerror(errno) );
 				break;
 			}
 
@@ -241,7 +229,7 @@ do_ar(int argc, const char ** argv)
 		}
 		else
 		{
-			fprintf(stderr, "Oops -- I don't know what to do\n");
+			errmsg( "ar: Oops -- I don't know what to do..." );
 			break;
 		}
 	}
@@ -264,9 +252,7 @@ createFile(const Archive * arch)
 
 	if (fd == -1)
 	{
-		fprintf(stderr, "Can't create \"%s\": %s\n",
-			arch->name, strerror(errno));
-
+		errmsg( "ar: cannot create \"%s\": %s", arch->name, strerror(errno) );
 		return -1;
 	}
 
@@ -288,7 +274,7 @@ createFile(const Archive * arch)
  * file names, or else that the specified list of file names is empty.
  */
 static BOOL
-wantMember(const Archive * arch, int n_names, const char ** name)
+wantMember(const Archive * arch, int n_names, const char *const *name)
 {
 	int	i;
 
@@ -395,9 +381,7 @@ openArchive(const char * name, Archive * arch)
 
 	if (arch->fd == -1)
 	{
-		fprintf(stderr, "Can't open archive file %s: %s\n",
-			name, strerror(errno));
-
+		errmsg( "ar: cannot open archive file %s: %s", name, strerror(errno) );
 		return FALSE;
 	}
 
@@ -405,23 +389,19 @@ openArchive(const char * name, Archive * arch)
 
 	if (cc == -1)
 	{
-		fprintf(stderr, "Error reading archive header: %s\n",
-			strerror(errno));
-
+		errmsg( "ar: %s: error reading archive header: %s", name, strerror(errno) );
 		goto close_and_out;
 	} 
 
 	if (cc != SARMAG)
 	{
-		fprintf(stderr, "Short read of archive header\n");
-
+		errmsg( "ar: %s: archive header too short", name );
 		goto close_and_out;
 	}
 
 	if (memcmp(buf, ARMAG, SARMAG))
 	{
-		fprintf(stderr, "Invalid archive header\n");
-
+		errmsg( "ar: %s: invalid archive header", name );
 		goto close_and_out;
 	}
 
@@ -472,9 +452,7 @@ readMember(Archive * arch, struct ar_hdr * hdr)
 
 	if (cc < 0)
 	{
-		fprintf(stderr, "Error reading member header: %s\n",
-			strerror(errno));
-
+		errmsg( "ar: error reading member header: %s", strerror(errno) );
 		return FALSE;
 	}
 
@@ -487,15 +465,13 @@ readMember(Archive * arch, struct ar_hdr * hdr)
 
 	if (cc != sizeof(*hdr))
 	{
-		fprintf(stderr, "Short read of member header\n");
-
+		errmsg( "ar: member header too short" );
 		return FALSE;
 	}
 
 	if (memcmp(hdr->ar_fmag, ARFMAG, sizeof(hdr->ar_fmag)))
 	{
-		fprintf(stderr, "Invalid member header\n");
-
+		errmsg( "ar: invalid member header" );
 		return FALSE;
 	}
 
@@ -585,7 +561,7 @@ shortNameMatchesSysV(const char * name)
 		arch->name = malloc(n);				\
 		if (!arch->name)				\
 		{						\
-			fprintf(stderr, "Out of memory\n");	\
+			errmsg( "malloc: %s", strerror(errno) );	\
 			return FALSE;				\
 		}						\
 	} while (0);
@@ -631,16 +607,13 @@ canonicalize(Archive * arch, const struct ar_hdr * hdr)
 
 		if (cc == -1)
 		{
-			fprintf(stderr, "Error reading longname: %s\n",
-				strerror(errno));
-
+			errmsg( "ar: error reading longname: %s", strerror(errno) );
 			goto free_and_out;
 		}
 
 		if (cc != n)
 		{
-			fprintf(stderr, "Unexpected end of file in longname\n");
-
+			errmsg( "ar: unexpected end of file in longname" );
 			goto free_and_out;
 		}
 
@@ -663,8 +636,7 @@ canonicalize(Archive * arch, const struct ar_hdr * hdr)
 
 		if (n >= strlen(arch->nameTable))
 		{
-			fprintf(stderr, "Longname index too large\n");
-
+			errmsg( "ar: longname index too large" );
 			return FALSE;
 		}
 
@@ -674,15 +646,13 @@ canonicalize(Archive * arch, const struct ar_hdr * hdr)
 
 		if (!p)
 		{
-			fprintf(stderr, "Bad longname index\n");
-
+			errmsg( "ar: invalid longname index" );
 			return FALSE;
 		}
 
 		if (p[1] != '\n')
 		{
-			fprintf(stderr, "Malformed longname table\n");
-
+			errmsg( "ar: malformed longname table" );
 			return FALSE;
 		}
 
@@ -724,7 +694,7 @@ canonicalize(Archive * arch, const struct ar_hdr * hdr)
 #define FIELD(AFIELD, MFIELD, base)					\
 	if (!getNumber(hdr->AFIELD, base, sizeof(hdr->AFIELD), &ul))	\
 	{								\
-		fprintf(stderr, "Malformed archive member header\n");	\
+		errmsg( "ar: malformed archive member header" );	\
 		goto free_and_out;					\
 	}								\
 	arch->MFIELD = ul;
@@ -790,9 +760,7 @@ skipPadding(int fd, int pad)
 	{
 		if (lseek(fd, 1, SEEK_CUR) == -1)
 		{
-			fprintf(stderr, "Can't skip pad byte: %s\n",
-				strerror(errno));
-
+			errmsg( "ar: cannot skip pad byte: %s", strerror(errno) );
 			return FALSE;
 		}
 	}
@@ -842,8 +810,7 @@ readSpecialMember(Archive * arch)
 
 		if (!getNumber(hdr.ar_size, 10, sizeof(hdr.ar_size), &len))
 		{
-			fprintf(stderr, "Invalid name-table size\n");
-
+			errmsg( "ar: invalid nameTable size" );
 			return FALSE;
 		}
 
@@ -851,8 +818,7 @@ readSpecialMember(Archive * arch)
 
 		if (!arch->nameTable)
 		{
-			fprintf(stderr, "Out of memory\n");
-
+			errmsg( "ar: nameTable: %s", strerror(errno) );
 			return FALSE;
 		}
 
@@ -860,17 +826,13 @@ readSpecialMember(Archive * arch)
 
 		if (cc == -1)
 		{
-			fprintf(stderr, "Error reading name-table: %s\n",
-				strerror(errno));
-
+			errmsg( "ar: error reading nameTable: %s", strerror(errno) );
 			return FALSE;
 		}
 
 		if (cc != (ssize_t) len)
 		{
-			fprintf(stderr,
-				"Unexpected end of file in name-table\n");
-
+			errmsg( "ar: unexpected end of file in nameTable" );
 			return FALSE;
 		}
 
@@ -932,9 +894,7 @@ skipMember(const Archive * arch)
 {
 	if (lseek(arch->fd, arch->size, SEEK_CUR) == -1)
 	{
-		fprintf(stderr, "Can't skip past archive member: %s\n",
-			strerror(errno));
-
+		errmsg( "ar: cannot skip past archive member: %s", strerror(errno) );
 		return FALSE;
 	}
 
@@ -962,23 +922,19 @@ writeFile(const Archive * arch, int outfd)
 
 		if (cc == -1)
 		{
-			fprintf(stderr, "Error reading archive member: %s\n",
-				strerror(errno));
-
+			errmsg( "ar: error reading archive member: %s", strerror(errno) );
 			return FALSE;
 		}
 
 		if (cc == 0)
 		{
-			fprintf(stderr, "Unexpected end of file\n");
-
+			errmsg( "ar: unexpected end of file" );
 			return FALSE;
 		}
 
 		if (fullWrite(outfd, buf, cc) < 0)
 		{
-			fprintf(stderr, "Write error: %s\n", strerror(errno));
-
+			errmsg( "ar: write failure: %s", strerror(errno) );
 			return FALSE;
 		}
 
--- sash/cmd_chattr.c
+++ sash/cmd_chattr.c
@@ -12,8 +12,9 @@
 
 #include <sys/ioctl.h>
 #include <sys/types.h>
-#include <linux/ext2_fs.h>
+#include <ext2fs/ext2_fs.h>
 
+#include "message.h"
 #include "sash.h"
 
 
@@ -22,7 +23,7 @@
  * This can turn on or off the immutable and append-only ext2 flags.
  */
 void
-do_chattr(int argc, const char ** argv)
+do_chattr(int argc, const char *const *argv)
 {
 	const char *	fileName;
 	const char *	options;
@@ -71,9 +72,7 @@ do_chattr(int argc, const char ** argv)
 					break;
 
 				default:
-					fprintf(stderr, "Unknown flag '%c'\n",
-						options[-1]);
-
+					errmsg( "chattr: unknown flag: %c", options[-1] );
 					return;
 			}
 		}
@@ -84,15 +83,13 @@ do_chattr(int argc, const char ** argv)
 	 */
 	if ((onFlags == 0) && (offFlags == 0))
 	{
-		fprintf(stderr, "No attributes specified\n");
-
+		errmsg( "chattr: no attributes specified" );
 		return;
 	}
 
 	if ((onFlags & offFlags) != 0)
 	{
-		fprintf(stderr, "Inconsistent attributes specified\n");
-
+		errmsg( "chattr: inconsistent attributes specified" );
 		return;
 	}
 
@@ -101,8 +98,7 @@ do_chattr(int argc, const char ** argv)
 	 */
 	if (argc <= 0)
 	{
-		fprintf(stderr, "No files specified for setting attributes\n");
-
+		errmsg( "chattr: no files specified for setting attributes" );
 		return;
 	}
 
@@ -120,8 +116,7 @@ do_chattr(int argc, const char ** argv)
 
 		if (fd < 0)
 		{
-			perror(fileName);
-
+			errmsg( "chattr: %s: %s", fileName, strerror(errno) );
 			continue;
 		}
 
@@ -130,10 +125,8 @@ do_chattr(int argc, const char ** argv)
 		 */
 		if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldFlags) < 0)
 		{
-			perror(fileName);
-
-			(void) close(fd);
-
+			errmsg( "chattr: %s: %s", fileName, strerror(errno) );
+			close(fd);
 			continue;
 		}
 
@@ -160,10 +153,8 @@ do_chattr(int argc, const char ** argv)
 		 */
 		if (ioctl(fd, EXT2_IOC_SETFLAGS, &newFlags) < 0)
 		{
-			perror(fileName);
-
+			errmsg( "chattr: %s: %s", fileName, strerror(errno) );
 			(void) close(fd);
-
 			continue;
 		}
 
@@ -180,7 +171,7 @@ do_chattr(int argc, const char ** argv)
  * This lists the immutable and append-only ext2 flags.
  */
 void
-do_lsattr(int argc, const char ** argv)
+do_lsattr(int argc, const char *const *argv)
 {
 	const char *	fileName;
 	int		fd;
@@ -205,8 +196,7 @@ do_lsattr(int argc, const char ** argv)
 
 		if (fd < 0)
 		{
-			perror(fileName);
-
+			errmsg( "lsattr: %s: %s", fileName, strerror(errno) );
 			continue;
 		}
 
@@ -222,8 +212,7 @@ do_lsattr(int argc, const char ** argv)
 
 		if (status < 0)
 		{
-			perror(fileName);
-
+			errmsg( "lsattr: %s: %s", fileName, strerror(errno) );
 			continue;
 		}
 
--- sash/cmd_dd.c
+++ sash/cmd_dd.c
@@ -6,6 +6,7 @@
  * The "dd" built-in command.
  */
 
+#include "message.h"
 #include "sash.h"
 
 
@@ -41,7 +42,7 @@ static	long	getNum(const char * cp);
 
 
 void
-do_dd(int argc, const char ** argv)
+do_dd(int argc, const char *const *argv)
 {
 	const char *	str;
 	const PARAM *	par;
@@ -73,10 +74,9 @@ do_dd(int argc, const char ** argv)
 		str = *++argv;
 		cp = strchr(str, '=');
 
-		if (cp == NULL)
+		if ( !cp )
 		{
-			fprintf(stderr, "Bad dd argument\n");
-
+			errmsg( "dd: invalid argument" );
 			return;
 		}
 
@@ -93,8 +93,7 @@ do_dd(int argc, const char ** argv)
 			case PAR_IF:
 				if (inFile)
 				{
-					fprintf(stderr, "Multiple input files illegal\n");
-
+					errmsg( "dd: multiple input files illegal" );
 					return;
 				}
 	
@@ -104,8 +103,7 @@ do_dd(int argc, const char ** argv)
 			case PAR_OF:
 				if (outFile)
 				{
-					fprintf(stderr, "Multiple output files illegal\n");
-
+					errmsg( "dd: multiple output files illegal" );
 					return;
 				}
 
@@ -117,8 +115,7 @@ do_dd(int argc, const char ** argv)
 
 				if (blockSize <= 0)
 				{
-					fprintf(stderr, "Bad block size value\n");
-
+					errmsg( "dd: bad block size value" );
 					return;
 				}
 
@@ -129,8 +126,7 @@ do_dd(int argc, const char ** argv)
 
 				if (count < 0)
 				{
-					fprintf(stderr, "Bad count value\n");
-
+					errmsg( "dd: bad count value" );
 					return;
 				}
 
@@ -141,8 +137,7 @@ do_dd(int argc, const char ** argv)
 
 				if (seekVal < 0)
 				{
-					fprintf(stderr, "Bad seek value\n");
-
+					errmsg( "dd: bad seek value" );
 					return;
 				}
 
@@ -153,31 +148,27 @@ do_dd(int argc, const char ** argv)
 
 				if (skipVal < 0)
 				{
-					fprintf(stderr, "Bad skip value\n");
-
+					errmsg( "dd: bad skip value" );
 					return;
 				}
 
 				break;
 
 			default:
-				fprintf(stderr, "Unknown dd parameter\n");
-
+				errmsg( "dd: unknown parameter" );
 				return;
 		}
 	}
 
-	if (inFile == NULL)
+	if ( !inFile )
 	{
-		fprintf(stderr, "No input file specified\n");
-
+		errmsg( "dd: no input file specified" );
 		return;
 	}
 
-	if (outFile == NULL)
+	if ( !outFile )
 	{
-		fprintf(stderr, "No output file specified\n");
-
+		errmsg( "dd: no output file specified" );
 		return;
 	}
 
@@ -185,12 +176,11 @@ do_dd(int argc, const char ** argv)
 
 	if (blockSize > sizeof(localBuf))
 	{
-		buf = malloc(blockSize);
+		buf = alloca(blockSize);
 
-		if (buf == NULL)
+		if ( !buf )
 		{
-			fprintf(stderr, "Cannot allocate buffer\n");
-
+			errmsg( "dd: buffer allocation failure: %s", strerror(errno) );
 			return;
 		}
 	}
@@ -202,11 +192,7 @@ do_dd(int argc, const char ** argv)
 
 	if (inFd < 0)
 	{
-		perror(inFile);
-
-		if (buf != localBuf)
-			free(buf);
-
+		errmsg( "dd: error opening %s: %s", inFile, strerror(errno) );
 		return;
 	}
 
@@ -214,12 +200,9 @@ do_dd(int argc, const char ** argv)
 
 	if (outFd < 0)
 	{
-		perror(outFile);
+		errmsg( "dd: error opening %s: %s", outFile, strerror(errno) );
 		close(inFd);
 
-		if (buf != localBuf)
-			free(buf);
-
 		return;
 	}
 
@@ -233,13 +216,13 @@ do_dd(int argc, const char ** argv)
 
 				if (inCc < 0)
 				{
-					perror(inFile);
+					errmsg( "dd: error reading %s while skipping: %s", inFile, strerror(errno) );
 					goto cleanup;
 				}
 
 				if (inCc == 0)
 				{
-					fprintf(stderr, "End of file while skipping\n");
+					errmsg( "dd: %s: unexpected end of file while skipping", inFile );
 					goto cleanup;
 				}
 			}
@@ -250,8 +233,7 @@ do_dd(int argc, const char ** argv)
 	{
 		if (lseek(outFd, seekVal * blockSize, 0) < 0)
 		{
-			perror(outFile);
-
+			errmsg( "dd: error seeking %s: %s", outFile, strerror(errno) );
 			goto cleanup;
 		}
 	}
@@ -263,7 +245,7 @@ do_dd(int argc, const char ** argv)
 
 		if (intFlag)
 		{
-			fprintf(stderr, "Interrupted\n");
+			errmsg( "dd: interrupted" );
 			goto cleanup;
 		}
 
@@ -273,7 +255,7 @@ do_dd(int argc, const char ** argv)
 
 			if (outCc < 0)
 			{
-				perror(outFile);
+				errmsg( "dd: error writing %s: %s", outFile, strerror(errno) );
 				goto cleanup;
 			}
 
@@ -284,22 +266,23 @@ do_dd(int argc, const char ** argv)
 	}
 
 	if (inCc < 0)
-		perror(inFile);
+		errmsg( "dd: error reading %s: %s", inFile, strerror(errno) );
 
 cleanup:
-	close(inFd);
+	if ( close(inFd) < 0 )
+		errmsg( "dd: error closing %s: %s", inFile, strerror(errno) );
 
 	if (close(outFd) < 0)
-		perror(outFile);
-
-	if (buf != localBuf)
-		free(buf);
+		errmsg( "dd: error closing %s: %s", outFile, strerror(errno) );
 
-	printf("%ld+%d records in\n", intotal / blockSize,
-		(intotal % blockSize) != 0);
+	if ( intotal || outTotal )
+	{
+		printf("%ld+%d records in\n", intotal / blockSize,
+			(intotal % blockSize) != 0);
 
-	printf("%ld+%d records out\n", outTotal / blockSize,
-		(outTotal % blockSize) != 0);
+		printf("%ld+%d records out\n", outTotal / blockSize,
+			(outTotal % blockSize) != 0);
+	}
 }
 
 
--- sash/cmd_ed.c
+++ sash/cmd_ed.c
@@ -6,6 +6,7 @@
  * The "ed" built-in command (much simplified)
  */
 
+#include "message.h"
 #include "sash.h"
 
 #define	USERSIZE	1024	/* max line length typed in by user */
@@ -43,7 +44,7 @@ static	LEN	bufSize;
 
 static	void	doCommands(void);
 static	void	subCommand(const char * cmd, NUM num1, NUM num2);
-static	BOOL	getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum);
+static	BOOL	getNum(const char **retcp, BOOL * retHaveNum, NUM * retNum);
 static	BOOL	setCurNum(NUM num);
 static	BOOL	initEdit(void);
 static	void	termEdit(void);
@@ -61,7 +62,7 @@ static	LEN	findString
 
 
 void
-do_ed(int argc, const char ** argv)
+do_ed(int argc, const char *const *argv)
 {
 	if (!initEdit())
 		return;
@@ -70,18 +71,16 @@ do_ed(int argc, const char ** argv)
 	{
 		fileName = strdup(argv[1]);
 
-		if (fileName == NULL)
+		if ( !fileName )
 		{
-			fprintf(stderr, "No memory\n");
+			errmsg( "ed: %s", strerror(errno) );
 			termEdit();
-
 			return;
 		}
 
 		if (!readLines(fileName, 1))
 		{
 			termEdit();
-
 			return;
 		}
 
@@ -119,7 +118,7 @@ doCommands(void)
 		printf(": ");
 		fflush(stdout);
 
-		if (fgets(buf, sizeof(buf), stdin) == NULL)
+		if ( !fgets(buf, sizeof(buf), stdin) )
 			return;
 
 		len = strlen(buf);
@@ -131,7 +130,7 @@ doCommands(void)
 
 		if (*endbuf != '\n')
 		{
-			fprintf(stderr, "Command line too long\n");
+			errmsg( "ed: command line too long" );
 
 			do
 			{
@@ -208,7 +207,7 @@ doCommands(void)
 			case 'f':
 				if (*cp && !isBlank(*cp))
 				{
-					fprintf(stderr, "Bad file command\n");
+					errmsg( "ed: bad file command" );
 					break;
 				}
 
@@ -227,9 +226,9 @@ doCommands(void)
 
 				newname = strdup(cp);
 
-				if (newname == NULL)
+				if ( !newname )
 				{
-					fprintf(stderr, "No memory for file name\n");
+					errmsg( "ed: file name allocation failure: %s", strerror(errno) );
 					break;
 				}
 
@@ -249,7 +248,7 @@ doCommands(void)
 
 				if ((*cp < 'a') || (*cp > 'a') || cp[1])
 				{
-					fprintf(stderr, "Bad mark name\n");
+					errmsg( "ed: bad mark name" );
 					break;
 				}
 
@@ -270,7 +269,7 @@ doCommands(void)
 
 				if (have1 || *cp)
 				{
-					fprintf(stderr, "Bad quit command\n");
+					errmsg( "ed: bad quit command" );
 					break;
 				}
 
@@ -295,16 +294,16 @@ doCommands(void)
 			case 'r':
 				if (*cp && !isBlank(*cp))
 				{
-					fprintf(stderr, "Bad read command\n");
+					errmsg( "ed: bad read command" );
 					break;
 				}
 
 				while (isBlank(*cp))
 					cp++;
 
-				if (*cp == '\0')
+				if ( !*cp )
 				{
-					fprintf(stderr, "No file name\n");
+					errmsg( "ed: no file name specified" );
 					break;
 				}
 
@@ -314,7 +313,7 @@ doCommands(void)
 				if (readLines(cp, num1 + 1))
 					break;
 
-				if (fileName == NULL)
+				if ( !fileName )
 					fileName = strdup(cp);
 
 				break;
@@ -326,7 +325,7 @@ doCommands(void)
 			case 'w':
 				if (*cp && !isBlank(*cp))
 				{
-					fprintf(stderr, "Bad write command\n");
+					errmsg( "ed: bad write command" );
 					break;
 				}
 
@@ -341,9 +340,9 @@ doCommands(void)
 				if (*cp == '\0')
 					cp = fileName;
 
-				if (cp == NULL)
+				if ( !cp )
 				{
-					fprintf(stderr, "No file name specified\n");
+					errmsg( "ed: no file name specified");
 					break;
 				}
 	
@@ -368,7 +367,7 @@ doCommands(void)
 			case '.':
 				if (have1)
 				{
-					fprintf(stderr, "No arguments allowed\n");
+					errmsg( "ed: no arguments allowed" );
 					break;
 				}
 
@@ -398,7 +397,7 @@ doCommands(void)
 				break;
 
 			default:
-				fprintf(stderr, "Unimplemented command\n");
+				errmsg( "ed: command not implemented" );
 				break;
 		}
 	}
@@ -430,8 +429,7 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 
 	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
 	{
-		fprintf(stderr, "Bad line range for substitute\n");
-
+		errmsg( "ed: bad line range for substitution" );
 		return;
 	}
 
@@ -448,8 +446,7 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 
 	if (isBlank(*cp) || (*cp == '\0'))
 	{
-		fprintf(stderr, "Bad delimiter for substitute\n");
-
+		errmsg( "ed: bad delimiter for substitution" );
 		return;
 	}
 
@@ -458,10 +455,9 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 
 	cp = strchr(cp, delim);
 
-	if (cp == NULL)
+	if ( !cp )
 	{
-		fprintf(stderr, "Missing 2nd delimiter for substitute\n");
-
+		errmsg( "ed: missing 2nd delimiter for substitution" );
 		return;
 	}
 
@@ -486,8 +482,7 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 			break;
 
 		default:
-			fprintf(stderr, "Unknown option for substitute\n");
-
+			errmsg( "ed: unknown option for substitution" );
 			return;
 	}
 
@@ -495,7 +490,7 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 	{
 		if (searchString[0] == '\0')
 		{
-			fprintf(stderr, "No previous search string\n");
+			errmsg( "ed: no previous search string" );
 
 			return;
 		}
@@ -508,7 +503,7 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 
 	lp = findLine(num1);
 
-	if (lp == NULL)
+	if ( !lp )
 		return;
 
 	oldLen = strlen(oldStr);
@@ -581,10 +576,9 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 		 */
 		nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
 
-		if (nlp == NULL)
+		if ( !nlp )
 		{
-			fprintf(stderr, "Cannot get memory for line\n");
-
+			errmsg( "ed: cannot allocate line: %s", strerror(errno) );
 			return;
 		}
 
@@ -625,7 +619,7 @@ subCommand(const char * cmd, NUM num1, NUM num2)
 	}
 
 	if (!didSub)
-		fprintf(stderr, "No substitutions found for \"%s\"\n", oldStr);
+		errmsg( "ed: no substitutions found for \"%s\"", oldStr );
 }
 
 
@@ -647,7 +641,7 @@ findString( const LINE * lp, const char * str, LEN len, LEN offset)
 	{
 		ncp = memchr(cp, *str, left);
 
-		if (ncp == NULL)
+		if ( !ncp )
 			return -1;
 
 		left -= (ncp - cp);
@@ -692,7 +686,7 @@ addLines(NUM num)
 
 		if (buf[len - 1] != '\n')
 		{
-			fprintf(stderr, "Line too long\n");
+			errmsg( "ed: line too long" );
 
 			do
 			{
@@ -718,7 +712,7 @@ addLines(NUM num)
  * The character pointer which stopped the scan is also returned.
  */
 static BOOL
-getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
+getNum(const char **retcp, BOOL * retHaveNum, NUM * retNum)
 {
 	const char *	cp;
 	char *		endStr;
@@ -757,7 +751,7 @@ getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
 
 				if ((*cp < 'a') || (*cp > 'z'))
 				{
-					fprintf(stderr, "Bad mark name\n");
+					errmsg( "ed: bad mark name" );
 
 					return FALSE;
 				}
@@ -844,10 +838,9 @@ initEdit(void)
 	bufSize = INITBUF_SIZE;
 	bufBase = malloc(bufSize);
 
-	if (bufBase == NULL)
+	if ( !bufBase )
 	{
-		fprintf(stderr, "No memory for buffer\n");
-
+		errmsg( "ed: buffer allocation failure: %s", strerror(errno) );
 		return FALSE;
 	}
 
@@ -917,8 +910,7 @@ readLines(const char * file, NUM num)
 
 	if ((num < 1) || (num > lastNum + 1))
 	{
-		fprintf(stderr, "Bad line for read\n");
-
+		errmsg( "ed: bad line for read" );
 		return FALSE;
 	}
 
@@ -926,8 +918,7 @@ readLines(const char * file, NUM num)
 
 	if (fd < 0)
 	{
-		perror(file);
-
+		errmsg( "ed: error opening %s: %s", file, strerror(errno) );
 		return FALSE;
 	}
 
@@ -982,11 +973,10 @@ readLines(const char * file, NUM num)
 			len = (bufSize * 3) / 2;
 			cp = realloc(bufBase, len);
 
-			if (cp == NULL)
+			if ( !cp )
 			{
-				fprintf(stderr, "No memory for buffer\n");
+				errmsg( "ed: buffer allocation failure: %s", strerror(errno) );
 				close(fd);
-
 				return FALSE;
 			}
 
@@ -1004,9 +994,8 @@ readLines(const char * file, NUM num)
 
 	if (cc < 0)
 	{
-		perror(file);
+		errmsg( "ed: error reading %s: %s", file, strerror(errno) );
 		close(fd);
-
 		return FALSE;
 	}
 
@@ -1046,7 +1035,7 @@ writeLines(const char * file, NUM num1, NUM num2)
 
 	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
 	{
-		fprintf(stderr, "Bad line range for write\n");
+		errmsg( "ed: bad line range for write" );
 
 		return FALSE;
 	}
@@ -1057,8 +1046,7 @@ writeLines(const char * file, NUM num1, NUM num2)
 	fd = creat(file, 0666);
 
 	if (fd < 0) {
-		perror(file);
-
+		errmsg( "ed: error creating %s: %s", file, strerror(errno) );
 		return FALSE;
 	}
 
@@ -1067,10 +1055,9 @@ writeLines(const char * file, NUM num1, NUM num2)
 
 	lp = findLine(num1);
 
-	if (lp == NULL)
+	if ( !lp )
 	{
 		close(fd);
-
 		return FALSE;
 	}
 
@@ -1078,9 +1065,8 @@ writeLines(const char * file, NUM num1, NUM num2)
 	{
 		if (write(fd, lp->data, lp->len) != lp->len)
 		{
-			perror(file);
+			errmsg( "ed: error writing %s: %s", file, strerror(errno) );
 			close(fd);
-
 			return FALSE;
 		}
 
@@ -1091,8 +1077,7 @@ writeLines(const char * file, NUM num1, NUM num2)
 
 	if (close(fd) < 0)
 	{
-		perror(file);
-
+		errmsg( "ed: error closing %s: %s", file, strerror(errno) );
 		return FALSE;
 	}
 
@@ -1118,14 +1103,13 @@ printLines(NUM num1, NUM num2, BOOL expandFlag)
 
 	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
 	{
-		fprintf(stderr, "Bad line range for print\n");
-
+		errmsg( "ed: bad line range for print" );
 		return FALSE;
 	}
 
 	lp = findLine(num1);
 
-	if (lp == NULL)
+	if ( !lp )
 		return FALSE;
 
 	while (!intFlag && (num1 <= num2))
@@ -1199,17 +1183,15 @@ insertLine(NUM num, const char * data, LEN len)
 
 	if ((num < 1) || (num > lastNum + 1))
 	{
-		fprintf(stderr, "Inserting at bad line number\n");
-
+		errmsg( "ed: inserting at bad line number" );
 		return FALSE;
 	}
 
 	newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
 
-	if (newLp == NULL) 
+	if ( !newLp ) 
 	{
-		fprintf(stderr, "Failed to allocate memory for line\n");
-
+		errmsg( "ed: line allocation failure: %s", strerror(errno) );
 		return FALSE;
 	}
 
@@ -1222,7 +1204,7 @@ insertLine(NUM num, const char * data, LEN len)
 	{
 		lp = findLine(num);
 
-		if (lp == NULL)
+		if ( !lp )
 		{
 			free((char *) newLp);
 
@@ -1255,14 +1237,13 @@ deleteLines(NUM num1, NUM num2)
 
 	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
 	{
-		fprintf(stderr, "Bad line numbers for delete\n");
-
+		errmsg( "ed: bad line numbers for delete" );
 		return FALSE;
 	}
 
 	lp = findLine(num1);
 
-	if (lp == NULL)
+	if ( !lp )
 		return FALSE;
 
 	if ((curNum >= num1) && (curNum <= num2))
@@ -1316,7 +1297,7 @@ searchLines(const char * str, NUM num1, NUM num2)
 
 	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
 	{
-		fprintf(stderr, "Bad line numbers for search\n");
+		errmsg( "ed: bad line numbers for search" );
 
 		return 0;
 	}
@@ -1325,7 +1306,7 @@ searchLines(const char * str, NUM num1, NUM num2)
 	{
 		if (searchString[0] == '\0')
 		{
-			fprintf(stderr, "No previous search string\n");
+			errmsg( "ed: no previous search string" );
 
 			return 0;
 		}
@@ -1340,7 +1321,7 @@ searchLines(const char * str, NUM num1, NUM num2)
 
 	lp = findLine(num1);
 
-	if (lp == NULL)
+	if ( !lp )
 		return 0;
 
 	while (num1 <= num2)
@@ -1352,8 +1333,7 @@ searchLines(const char * str, NUM num1, NUM num2)
 		lp = lp->next;
 	}
 
-	fprintf(stderr, "Cannot find string \"%s\"\n", str);
-
+	errmsg( "ed: cannot find string \"%s\"", str );
 	return 0;
 }
 
@@ -1369,8 +1349,7 @@ findLine(NUM num)
 
 	if ((num < 1) || (num > lastNum))
 	{
-		fprintf(stderr, "Line number %d does not exist\n", num);
-
+		errmsg( "ed: line number %d does not exist", num );
 		return NULL;
 	}
 
@@ -1424,7 +1403,7 @@ setCurNum(NUM num)
 
 	lp = findLine(num);
 
-	if (lp == NULL)
+	if ( !lp )
 		return FALSE;
 
 	curNum = num;
--- sash/cmd_file.c
+++ sash/cmd_file.c
@@ -6,11 +6,10 @@
  * The "file" built-in command.
  */
 
-#include <ctype.h>
-#include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include "message.h"
 #include "sash.h"
 
 
@@ -18,7 +17,7 @@ static const char *	checkFile(const char * name);
 
 
 void
-do_file(int argc, const char ** argv)
+do_file(int argc, const char *const *argv)
 {
 	const char *	name;
 	const char *	info;
--- sash/cmd_find.c
+++ sash/cmd_find.c
@@ -9,18 +9,11 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <dirent.h>
-#include <errno.h>
 
+#include "message.h"
 #include "sash.h"
 
 
-#ifdef	S_ISLNK
-#define	LSTAT	lstat
-#else
-#define	LSTAT	stat
-#endif
-
-
 #define	MAX_NAME_SIZE	(1024 * 10)
 
 
@@ -47,7 +40,7 @@ static	BOOL	testFile(const char * fullName, const struct stat * statBuf);
  * This is limited to just printing their file names.
  */
 void
-do_find(int argc, const char ** argv)
+do_find(int argc, const char *const *argv)
 {
 	const char *	cp;
 	const char *	path;
@@ -63,8 +56,7 @@ do_find(int argc, const char ** argv)
 
 	if ((argc <= 0) || (**argv == '-'))
 	{
-		fprintf(stderr, "No path specified\n");
-
+		errmsg( "find: no path specified" );
 		return;
 	}
 
@@ -82,7 +74,7 @@ do_find(int argc, const char ** argv)
 		{
 			if ((argc <= 0) || (**argv == '-'))
 			{
-				fprintf(stderr, "Missing type string\n");
+				errmsg( "find: missing type string" );
 
 				return;
 			}
@@ -94,7 +86,7 @@ do_find(int argc, const char ** argv)
 		{
 			if ((argc <= 0) || (**argv == '-'))
 			{
-				fprintf(stderr, "Missing file name\n");
+				errmsg( "find: missing file name" );
 
 				return;
 			}
@@ -106,7 +98,7 @@ do_find(int argc, const char ** argv)
 		{
 			if ((argc <= 0) || (**argv == '-'))
 			{
-				fprintf(stderr, "Missing file size\n");
+				errmsg( "find: missing file size" );
 
 				return;
 			}
@@ -121,7 +113,7 @@ do_find(int argc, const char ** argv)
 
 			if (*cp || (fileSize < 0))
 			{
-				fprintf(stderr, "Bad file size specified\n");
+				errmsg( "find: bad file size specified" );
 
 				return;
 			}
@@ -129,9 +121,9 @@ do_find(int argc, const char ** argv)
 		else
 		{
 			if (*cp != '-')
-				fprintf(stderr, "Missing dash in option\n");
+				errmsg( "find: missing dash in option" );
 			else
-				fprintf(stderr, "Unknown option\n");
+				errmsg( "find: unknown option" );
 
 			return;
 		}
@@ -143,16 +135,13 @@ do_find(int argc, const char ** argv)
 	 */
 	if (stat(path, &statBuf) < 0)
 	{
-		fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
-			strerror(errno));
-
+		errmsg( "find: cannot stat \"%s\": %s", path, strerror(errno) );
 		return;
 	}
 
 	if (!S_ISDIR(statBuf.st_mode))
 	{
-		fprintf(stderr, "Path \"%s\" is not a directory\n", path);
-
+		errmsg( "find: path \"%s\" is not a directory", path );
 		return;
 	}
 
@@ -191,11 +180,9 @@ examineDirectory(const char * path)
 	 */
 	dir = opendir(path);
 
-	if (dir == NULL)
+	if ( !dir )
 	{
-		fprintf(stderr, "Cannot read directory \"%s\": %s\n",
-			path, strerror(errno));
-
+		errmsg( "find: cannot read directory \"%s\": %s", path, strerror(errno) );
 		return;
 	}
 
@@ -229,11 +216,9 @@ examineDirectory(const char * path)
 		/*
 		 * Find out about this file.
 		 */
-		if (LSTAT(fullName, &statBuf) < 0)
+		if (lstat(fullName, &statBuf) < 0)
 		{
-			fprintf(stderr, "Cannot stat \"%s\": %s\n",
-				fullName, strerror(errno));
-
+			errmsg( "find: cannot stat \"%s\": %s", fullName, strerror(errno) );
 			continue;
 		}
 
--- sash/cmd_grep.c
+++ sash/cmd_grep.c
@@ -6,8 +6,7 @@
  * The "grep" built-in command.
  */
 
-#include <ctype.h>
-
+#include "message.h"
 #include "sash.h"
 
 
@@ -16,7 +15,7 @@ static	BOOL	search
 
 
 void
-do_grep(int argc, const char ** argv)
+do_grep(int argc, const char *const *argv)
 {
 	FILE *		fp;
 	const char *	word;
@@ -50,8 +49,7 @@ do_grep(int argc, const char ** argv)
 				break;
 
 			default:
-				fprintf(stderr, "Unknown option\n");
-
+				errmsg( "grep: unknown option: -%c", *cp );
 				return;
 		}
 	}
@@ -67,10 +65,9 @@ do_grep(int argc, const char ** argv)
 
 		fp = fopen(name, "r");
 
-		if (fp == NULL)
+		if ( !fp )
 		{
-			perror(name);
-
+			errmsg( "grep: error opening %s: %s", name, strerror(errno) );
 			continue;
 		}
 
@@ -81,7 +78,6 @@ do_grep(int argc, const char ** argv)
 			if (intFlag)
 			{
 				fclose(fp);
-
 				return;
 			}
 
@@ -90,7 +86,7 @@ do_grep(int argc, const char ** argv)
 			cp = &buf[strlen(buf) - 1];
 
 			if (*cp != '\n')
-				fprintf(stderr, "%s: Line too long\n", name);
+				errmsg( "grep: %s: line too long", name );
 
 			if (search(buf, word, ignoreCase))
 			{
@@ -105,7 +101,7 @@ do_grep(int argc, const char ** argv)
 		}
 
 		if (ferror(fp))
-			perror(name);
+			errmsg( "grep: error reading %s: %s", name, strerror(errno) );
 
 		fclose(fp);
 	}
--- sash/cmd_gzip.c
+++ sash/cmd_gzip.c
@@ -16,6 +16,7 @@
 #include <sys/stat.h>
 #include <zlib.h>
 
+#include "message.h"
 #include "sash.h"
 
 
@@ -63,7 +64,7 @@ static const char * convertName
 
 
 void
-do_gzip(int argc, const char ** argv)
+do_gzip(int argc, const char *const *argv)
 {
 	const char *	outPath;
 	const char *	inFile;
@@ -94,10 +95,10 @@ do_gzip(int argc, const char ** argv)
 	{
 		if (argv[i][0] == '-')
 		{
-			if (strcmp(argv[i], "-o") == 0)
-				fprintf(stderr, "Illegal use of -o\n");
+			if ( !strcmp(argv[i], "-o") )
+				errmsg( "gzip: illegal use of -o" );
 			else
-				fprintf(stderr, "Illegal option\n");
+				errmsg( "gzip: illegal option" );
 
 			return;
 		}
@@ -108,7 +109,7 @@ do_gzip(int argc, const char ** argv)
 	 * the input files in place using their full paths.  The input
 	 * file names are then deleted.
 	 */
-	if (outPath == NULL)
+	if ( !outPath )
 	{
 		while (!intFlag && (argc-- > 0))
 		{
@@ -128,8 +129,7 @@ do_gzip(int argc, const char ** argv)
 			 */
 			if (unlink(inFile) < 0)
 			{
-				fprintf(stderr, "%s: %s\n", inFile,
-					"Compressed ok but unlink failed");
+				errmsg( "gzip: %s: %s", inFile,	"compressed ok but unlink failed" );
 			}
 		}
 
@@ -147,7 +147,7 @@ do_gzip(int argc, const char ** argv)
 		if (argc == 1)
 			(void) gzip(*argv, outPath);
 		else
-			fprintf(stderr, "Exactly one input file is required\n");
+			errmsg( "gzip: exactly one input file is required" );
 
 		return;
 	}
@@ -193,7 +193,7 @@ do_gzip(int argc, const char ** argv)
 
 
 void
-do_gunzip(int argc, const char ** argv)
+do_gunzip(int argc, const char *const *argv)
 {
 	const char *	outPath;
 	const char *	inFile;
@@ -225,9 +225,9 @@ do_gunzip(int argc, const char ** argv)
 		if (argv[i][0] == '-')
 		{
 			if (strcmp(argv[i], "-o") == 0)
-				fprintf(stderr, "Illegal use of -o\n");
+				errmsg( "gunzip: illegal use of -o" );
 			else
-				fprintf(stderr, "Illegal option\n");
+				errmsg( "gunzip: illegal option" );
 
 			return;
 		}
@@ -249,9 +249,7 @@ do_gunzip(int argc, const char ** argv)
 
 			if (inFile == outFile)
 			{
-				fprintf(stderr, "%s: %s\n", inFile,
-					"missing compression extension");
-
+				errmsg( "gunzip: %s: %s", inFile, "missing compression suffix" );
 				continue;
 			}
 
@@ -267,8 +265,7 @@ do_gunzip(int argc, const char ** argv)
 			 */
 			if (unlink(inFile) < 0)
 			{
-				fprintf(stderr, "%s: %s\n", inFile,
-					"Uncompressed ok but unlink failed");
+				errmsg( "gunzip: %s: %s", inFile, "uncompressed ok but unlink failed" );
 			}
 		}
 
@@ -298,7 +295,7 @@ do_gunzip(int argc, const char ** argv)
 		if (argc == 1)
 			(void) gunzip(*argv, outPath);
 		else
-			fprintf(stderr, "Exactly one input file is required\n");
+			errmsg( "gunzip: exactly one input file is required" );
 
 		return;
 	}
@@ -367,8 +364,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 	 */
 	if (stat(inputFileName, &statBuf1) < 0)
 	{
-		perror(inputFileName);
-
+		errmsg( "gzip: %s: %s", inputFileName, strerror(errno) );
 		return FALSE;
 	}
 
@@ -381,10 +377,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 	if ((statBuf1.st_dev == statBuf2.st_dev) &&
 		(statBuf1.st_ino == statBuf2.st_ino))
 	{
-		fprintf(stderr,
-			"Cannot compress file \"%s\" on top of itself\n",
-			inputFileName);
-
+		errmsg( "gzip: cannot compress file \"%s\" on top of itself", inputFileName );
 		return FALSE;
 	}
 
@@ -395,8 +388,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 
 	if (inFD < 0)
 	{
-		perror(inputFileName);
-
+		errmsg( "gzip: error opening %s: %s", inputFileName, strerror(errno) );
 		goto failed;
 	}
 
@@ -407,8 +399,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 
 	if (outGZ == NULL)
 	{
-		fprintf(stderr, "%s: gzopen failed\n", outputFileName);
-
+		errmsg( "gzip: %s: gzopen failure: %s", outputFileName, strerror(errno) );
 		goto failed;
 	}
 
@@ -420,9 +411,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 	{
 		if (gzwrite(outGZ, buf, len) != len)
 		{
-			fprintf(stderr, "%s: %s\n", inputFileName,
-				gzerror(outGZ, &err));
-
+			errmsg( "gzip: error writing %s: %s", outputFileName, gzerror(outGZ, &err) );
 			goto failed;
 		}
 
@@ -432,8 +421,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 
 	if (len < 0)
 	{
-		perror(inputFileName);
-
+		errmsg( "gzip: error reading %s: %s", inputFileName, strerror(errno) );
 		goto failed;
 	}
 
@@ -442,8 +430,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 	 */
 	if (close(inFD))
 	{
-		perror(inputFileName);
-
+		errmsg( "gzip: error closing %s: %s", inputFileName, strerror(errno) );
 		goto failed;
 	}
 
@@ -451,8 +438,7 @@ gzip(const char * inputFileName, const char * outputFileName)
 
 	if (gzclose(outGZ) != Z_OK)
 	{
-		fprintf(stderr, "%s: gzclose failed\n", outputFileName);
-
+		errmsg( "gzip: %s: gzclose failure: %s", outputFileName, gzerror(outGZ, &err) );
 		goto failed;
 	}
 
@@ -502,8 +488,7 @@ gunzip(const char * inputFileName, const char * outputFileName)
 	 */
 	if (stat(inputFileName, &statBuf1) < 0)
 	{
-		perror(inputFileName);
-
+		errmsg( "gunzip: %s: %s", inputFileName, strerror(errno) );
 		return FALSE;
 	}
 
@@ -516,10 +501,7 @@ gunzip(const char * inputFileName, const char * outputFileName)
 	if ((statBuf1.st_dev == statBuf2.st_dev) &&
 		(statBuf1.st_ino == statBuf2.st_ino))
 	{
-		fprintf(stderr,
-			"Cannot uncompress file \"%s\" on top of itself\n",
-			inputFileName);
-
+		errmsg( "gunzip: cannot uncompress file \"%s\" on top of itself", inputFileName );
 		return FALSE;
 	}
 
@@ -528,10 +510,9 @@ gunzip(const char * inputFileName, const char * outputFileName)
 	 */
 	inGZ = gzopen(inputFileName, "rb");
 
-	if (inGZ == NULL)
+	if ( !inGZ )
 	{
-		fprintf(stderr, "%s: gzopen failed\n", inputFileName);
-
+		errmsg( "gunzip: %s: gzopen failure: %s", inputFileName, strerror(errno) );
 		return FALSE;
 	}
 
@@ -545,8 +526,7 @@ gunzip(const char * inputFileName, const char * outputFileName)
 
 	if (outFD < 0)
 	{
-		perror(outputFileName);
-
+		errmsg( "gunzip: error opening %s: %s", outputFileName, strerror(errno) );
 		goto failed;
 	}
 
@@ -558,8 +538,7 @@ gunzip(const char * inputFileName, const char * outputFileName)
 	{
 		if (fullWrite(outFD, buf, len) < 0)
 		{
-			perror(outputFileName);
-
+			errmsg( "gunzip: error writing %s: %s", outputFileName, strerror(errno) );
 			goto failed;
 		}
 
@@ -569,9 +548,7 @@ gunzip(const char * inputFileName, const char * outputFileName)
 
 	if (len < 0)
 	{
-		fprintf(stderr, "%s: %s\n", inputFileName,
-			gzerror(inGZ, &err));
-
+		errmsg( "gunzip: error reading %s: %s", inputFileName, gzerror(inGZ, &err) );
 		goto failed;
 	}
 
@@ -580,8 +557,7 @@ gunzip(const char * inputFileName, const char * outputFileName)
 	 */
 	if (close(outFD))
 	{
-		perror(outputFileName);
-
+		errmsg( "gunzip: error closing %s: %s", outputFileName, strerror(errno) );
 		goto failed;
 	}
 
@@ -589,8 +565,7 @@ gunzip(const char * inputFileName, const char * outputFileName)
 
 	if (gzclose(inGZ) != Z_OK)
 	{
-		fprintf(stderr, "%s: gzclose failed\n", inputFileName);
-
+		errmsg( "gunzip: %s: gzclose failure: %s", inputFileName, gzerror(inGZ, &err) );
 		goto failed;
 	}
 
--- sash/cmd_ls.c
+++ sash/cmd_ls.c
@@ -6,23 +6,17 @@
  * The "ls" built-in command.
  */
 
-#include "sash.h"
-
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <dirent.h>
 #include <pwd.h>
 #include <grp.h>
 
-
-#define	LISTSIZE	8192
+#include "message.h"
+#include "sash.h"
 
 
-#ifdef	S_ISLNK
-#define	LSTAT	lstat
-#else
-#define	LSTAT	stat
-#endif
+#define	LISTSIZE	8192
 
 
 /*
@@ -60,7 +54,7 @@ static	void	clearListNames(void);
 
 
 void
-do_ls(int argc, const char ** argv)
+do_ls(int argc, const char *const *argv)
 {
 	const char *	cp;
 	const char *	name;
@@ -73,7 +67,7 @@ do_ls(int argc, const char ** argv)
 	char		fullName[PATH_LEN];
 	struct	stat	statBuf;
 
-	static const char *	def[] = {"."};
+	static	const char	*const	def[] = {"."};
 
 	/*
 	 * Reset for a new listing run.
@@ -103,7 +97,7 @@ do_ls(int argc, const char ** argv)
 			case 'C':	flags |= LSF_COLUMN; break;
 
 			default:
-				fprintf(stderr, "Unknown option -%c\n", cp[-1]);
+				errmsg( "ls: unknown option -%c", cp[-1] );
 
 				return;
 		}
@@ -177,10 +171,9 @@ do_ls(int argc, const char ** argv)
 		name = *argv++;
 		endSlash = (*name && (name[strlen(name) - 1] == '/'));
 
-		if (LSTAT(name, &statBuf) < 0)
+		if ( lstat(name, &statBuf) < 0 )
 		{
-			perror(name);
-
+			errmsg( "ls: %s: %s", name, strerror(errno) );
 			continue;
 		}
 
@@ -197,8 +190,7 @@ do_ls(int argc, const char ** argv)
 
 		if (dirp == NULL)
 		{
-			perror(name);
-
+			errmsg( "ls: %s: %s", name, strerror(errno) );
 			continue;
 		}
 
@@ -300,10 +292,9 @@ listAllFiles(int flags, int displayWidth)
 	{
 		name = list[i];
 
-		if (LSTAT(name, &statBuf) < 0)
+		if ( lstat(name, &statBuf) < 0 )
 		{
-			perror(name);
-
+			errmsg( "ls: %s: %s", name, strerror(errno) );
 			continue;
 		}
 
@@ -529,10 +520,9 @@ addListName(const char * fileName)
 		newList = realloc(list,
 			((sizeof(char **)) * (listSize + LISTSIZE)));
 
-		if (newList == NULL)
+		if ( !newList )
 		{
-			fprintf(stderr, "No memory for file name buffer\n");
-
+			errmsg( "ls: file list allocation failure: %s", strerror(errno) );
 			return FALSE;
 		}
 
@@ -545,10 +535,9 @@ addListName(const char * fileName)
 	 */
 	list[listUsed] = strdup(fileName);
 
-	if (list[listUsed] == NULL)
+	if ( !list[listUsed] )
 	{
-		fprintf(stderr, "No memory for file name\n");
-
+		errmsg( "ls: file name allocation failure: %s", strerror(errno) );
 		return FALSE;
 	}
 
--- sash/cmd_tar.c
+++ sash/cmd_tar.c
@@ -7,12 +7,12 @@
  * This allows creation, extraction, and listing of tar files.
  */
 
-#include "sash.h"
-
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <dirent.h>
-#include <errno.h>
+
+#include "message.h"
+#include "sash.h"
 
 
 /*
@@ -87,13 +87,13 @@ static	ino_t		tarInode;
 /*
  * Local procedures to restore files from a tar file.
  */
-static	void	readTarFile(int fileCount, const char ** fileTable);
+static	void	readTarFile(int fileCount, const char *const *fileTable);
 static	void	readData(const char * cp, int count);
 static	void	createPath(const char * name, int mode);
 static	long	getOctal(const char * cp, int len);
 
 static	void	readHeader(const TarHeader * hp,
-			int fileCount, const char ** fileTable);
+			int fileCount, const char *const *fileTable);
 
 
 /*
@@ -108,19 +108,19 @@ static	void	saveDirectory(const char * fileName,
 			const struct stat * statbuf);
 
 static	BOOL	wantFileName(const char * fileName,
-			int fileCount, const char ** fileTable);
+			int fileCount, const char *const *fileTable);
 
 static	void	writeHeader(const char * fileName,
 			const struct stat * statbuf);
 
-static	void	writeTarFile(int fileCount, const char ** fileTable);
+static	void	writeTarFile(int fileCount, const char *const *fileTable);
 static	void	writeTarBlock(const char * buf, int len);
 static	BOOL	putOctal(char * cp, int len, long value);
 
 
 
 void
-do_tar(int argc, const char ** argv)
+do_tar(int argc, const char *const *argv)
 {
 	const char *	options;
 
@@ -129,8 +129,7 @@ do_tar(int argc, const char ** argv)
 
 	if (argc < 2)
 	{
-		fprintf(stderr, "Too few arguments for tar\n");
-
+		errmsg( "tar: too few arguments for tar" );
 		return;
 	}
 
@@ -154,10 +153,9 @@ do_tar(int argc, const char ** argv)
 		switch (*options)
 		{
 			case 'f':
-				if (tarName != NULL)
+				if ( !tarName )
 				{
-					fprintf(stderr, "Only one 'f' option allowed\n");
-
+					errmsg( "tar: only one 'f' option allowed" );
 					return;
 				}
 
@@ -183,8 +181,7 @@ do_tar(int argc, const char ** argv)
 				break;
 
 			default:
-				fprintf(stderr, "Unknown tar flag '%c'\n", *options);
-
+				errmsg( "tar: unknown flag: %c", *options );
 				return;
 		}
 	}
@@ -194,15 +191,13 @@ do_tar(int argc, const char ** argv)
 	 */
 	if (extractFlag + listFlag + createFlag != 1)
 	{
-		fprintf(stderr, "Exactly one of 'c', 'x' or 't' must be specified\n");
-
+		errmsg( "tar: exactly one of 'c', 'x' or 't' must be specified" );
 		return;
 	}
 
-	if (tarName == NULL)
+	if ( !tarName )
 	{
-		fprintf(stderr, "The 'f' flag must be specified\n");
-
+		errmsg( "tar: the 'f' flag must be specified" );
 		return;
 	}
 
@@ -222,7 +217,7 @@ do_tar(int argc, const char ** argv)
  * If the list is empty than all files are extracted or listed.
  */
 static void
-readTarFile(int fileCount, const char ** fileTable)
+readTarFile(int fileCount, const char *const *fileTable)
 {
 	const char *	cp;
 	int		cc;
@@ -248,8 +243,7 @@ readTarFile(int fileCount, const char ** fileTable)
 
 	if (tarFd < 0)
 	{
-		perror(tarName);
-
+		errmsg( "tar: error opening %s: %s", tarName, strerror(errno) );
 		return;
 	}
 
@@ -271,17 +265,13 @@ readTarFile(int fileCount, const char ** fileTable)
 
 			if (inCc < 0)
 			{
-				perror(tarName);
-
+				errmsg( "tar: error reading %s: %s", tarName, strerror(errno) );
 				goto done;
 			}
 
-			if (inCc == 0)
+			if ( !inCc )
 			{
-				fprintf(stderr,
-					"Unexpected end of file from \"%s\"",
-					tarName);
-
+				errmsg( "tar: %s: unexpected end of file", tarName );
 				goto done;
 			}
 		}
@@ -327,7 +317,7 @@ readTarFile(int fileCount, const char ** fileTable)
 	 * Check for an interrupt.
 	 */
 	if (intFlag)
-		fprintf(stderr, "Interrupted - aborting\n");
+		errmsg( "tar: interrupted" );
 
 
 done:
@@ -335,7 +325,7 @@ done:
 	 * Close the tar file if needed.
 	 */
 	if ((tarFd >= 0) && (close(tarFd) < 0))
-		perror(tarName);
+		errmsg( "tar: error closing %s: %s", tarName, strerror(errno) );
 
 	/*
 	 * Close the output file if needed.
@@ -353,7 +343,7 @@ done:
  * the end of the tar file.
  */
 static void
-readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
+readHeader(const TarHeader * hp, int fileCount, const char *const *fileTable)
 {
 	int		mode;
 	int		uid;
@@ -399,7 +389,7 @@ readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
 	if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0))
 	{
 		if (!badHeader)
-			fprintf(stderr, "Bad tar header, skipping\n");
+			errmsg( "tar: bad header, skipping" );
 
 		badHeader = TRUE;
 
@@ -436,10 +426,7 @@ readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
 			name++;
 
 		if (!warnedRoot)
-		{
-			fprintf(stderr,
-			"Absolute path detected, removing leading slashes\n");
-		}
+			errmsg( "tar: absolute path detected, removing leading slashes" );
 
 		warnedRoot = TRUE;
 	}
@@ -499,19 +486,14 @@ readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
 	if (hardLink)
 	{
 		if (link(hp->linkName, name) < 0)
-			perror(name);
-
+			errmsg( "tar: error hardlinking '%s' -> '%s': %s", name, hp->linkName, strerror(errno) );
 		return;
 	}
 
 	if (softLink)
 	{
-#ifdef	S_ISLNK
 		if (symlink(hp->linkName, name) < 0)
-			perror(name);
-#else
-		fprintf(stderr, "Cannot create symbolic links\n");
-#endif
+			errmsg( "tar: error symlinking '%s' -> '%s': %s", name, hp->linkName, strerror(errno) );
 		return;
 	}
 
@@ -541,9 +523,8 @@ readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
 
 	if (outFd < 0)
 	{
-		perror(name);
+		errmsg( "tar: error creating %s: %s", name, strerror(errno) );
 		skipFileFlag = TRUE;
-
 		return;
 	}
 
@@ -586,7 +567,7 @@ readData(const char * cp, int count)
 	 */
 	if (fullWrite(outFd, cp, count) < 0)
 	{
-		perror(outName);
+		errmsg( "tar: error writing %s: %s", outName, strerror(errno) );
 		(void) close(outFd);
 		outFd = -1;
 		skipFileFlag = TRUE;
@@ -601,7 +582,7 @@ readData(const char * cp, int count)
 	if (dataCc <= 0)
 	{
 		if (close(outFd))
-			perror(outName);
+			errmsg( "tar: error closing %s: %s", outName, strerror(errno) );
 
 		outFd = -1;
 	}
@@ -612,7 +593,7 @@ readData(const char * cp, int count)
  * Write a tar file containing the specified files.
  */
 static void
-writeTarFile(int fileCount, const char ** fileTable)
+writeTarFile(int fileCount, const char *const *fileTable)
 {
 	struct	stat	statbuf;
 
@@ -623,8 +604,7 @@ writeTarFile(int fileCount, const char ** fileTable)
 	 */
 	if (fileCount <= 0)
 	{
-		fprintf(stderr, "No files specified to be saved\n");
-
+		errmsg( "tar: no files specified to be saved" );
 		return;
 	}
 
@@ -635,8 +615,7 @@ writeTarFile(int fileCount, const char ** fileTable)
 
 	if (tarFd < 0)
 	{
-		perror(tarName);
-
+		errmsg( "tar: error creating %s: %s", tarName, strerror(errno) );
 		return;
 	}
 
@@ -645,8 +624,7 @@ writeTarFile(int fileCount, const char ** fileTable)
 	 */
 	if (fstat(tarFd, &statbuf) < 0)
 	{
-		perror(tarName);
-
+		errmsg( "tar: %s: %s", tarName, strerror(errno) );
 		goto done;
 	}
 
@@ -663,7 +641,7 @@ writeTarFile(int fileCount, const char ** fileTable)
 	}
 
 	if (intFlag)
-		fprintf(stderr, "Interrupted - aborting archiving\n");
+		errmsg( "tar: interrupted - archiving aborted");
 
 	/*
 	 * Now write an empty block of zeroes to end the archive.
@@ -676,7 +654,7 @@ done:
 	 * Close the tar file and check for errors if it was opened.
 	 */
 	if ((tarFd >= 0) && (close(tarFd) < 0))
-		perror(tarName);
+		errmsg( "tar: error closing %s: %s", tarName, strerror(errno) );
 }
 
 
@@ -702,8 +680,7 @@ saveFile(const char * fileName, BOOL seeLinks)
 	 */
 	if (strlen(fileName) >= TAR_NAME_SIZE)
 	{
-		fprintf(stderr, "%s: File name is too long\n", fileName);
-
+		errmsg( "tar: %s: file name is too long", fileName );
 		return;
 	}
 
@@ -719,8 +696,7 @@ saveFile(const char * fileName, BOOL seeLinks)
 
 	if (status < 0)
 	{
-		perror(fileName);
-
+		errmsg( "tar: %s: %s", fileName, strerror(errno) );
 		return;
 	}
 
@@ -729,7 +705,7 @@ saveFile(const char * fileName, BOOL seeLinks)
 	 */
 	if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode))
 	{
-		fprintf(stderr, "Skipping saving of archive file itself\n");
+		errmsg( "tar: skipped saving of archive file itself" );
 
 		return;
 	}
@@ -756,7 +732,7 @@ saveFile(const char * fileName, BOOL seeLinks)
 	/*
 	 * The file is a strange type of file, ignore it.
 	 */
-	fprintf(stderr, "%s: not a directory or regular file\n", fileName);
+	errmsg( "tar: %s: not a directory or regular file", fileName );
 }
 
 
@@ -780,8 +756,7 @@ saveRegularFile(const char * fileName, const struct stat * statbuf)
 
 	if (fileFd < 0)
 	{
-		perror(fileName);
-
+		errmsg( "tar: error opening %s: %s", fileName, strerror(errno) );
 		return;
 	}
 
@@ -823,7 +798,7 @@ saveRegularFile(const char * fileName, const struct stat * statbuf)
 
 			if (cc < 0)
 			{
-				perror(fileName);
+				errmsg( "tar: error reading %s: %s", fileName, strerror(errno) );
 
 				(void) close(fileFd);
 				errorFlag = TRUE;
@@ -837,10 +812,7 @@ saveRegularFile(const char * fileName, const struct stat * statbuf)
 			 */
 			if (cc < dataCount)
 			{
-				fprintf(stderr,
-					"%s: Short read - zero filling",
-					fileName);
-
+				errmsg( "tar: %s: unexpected end of file - zero filling", fileName );
 				sawEof = TRUE;
 			}
 		}
@@ -863,7 +835,7 @@ saveRegularFile(const char * fileName, const struct stat * statbuf)
 	 * Close the file.
 	 */
 	if (close(fileFd) < 0)
-		fprintf(stderr, "%s: close: %s\n", fileName, strerror(errno));
+		errmsg( "tar: error closing %s: %s", fileName, strerror(errno) );
 }
 
 
@@ -895,11 +867,9 @@ saveDirectory(const char * dirName, const struct stat * statbuf)
 	 */
 	dir = opendir(dirName);
 
-	if (dir == NULL)
+	if ( !dir )
 	{
-		fprintf(stderr, "Cannot read directory \"%s\": %s\n",
-			dirName, strerror(errno));
-
+		errmsg( "tar: cannot read directory \"%s\": %s\n", dirName, strerror(errno) );
 		return;
 	}
 
@@ -1029,10 +999,8 @@ writeTarBlock(const char * buf, int len)
 	 */
 	if ((completeLength > 0) && !fullWrite(tarFd, buf, completeLength))
 	{
-		perror(tarName);
-
+		errmsg( "tar: error writing %s: %s", tarName, strerror(errno) );
 		errorFlag = TRUE;
-
 		return;
 	}
 
@@ -1054,8 +1022,7 @@ writeTarBlock(const char * buf, int len)
 	 */
 	if (!fullWrite(tarFd, fullBlock, TAR_BLOCK_SIZE))
 	{
-		perror(tarName);
-
+		errmsg( "tar: error writing %s: %s", tarName, strerror(errno) );
 		errorFlag = TRUE;
 	}
 }
@@ -1191,7 +1158,7 @@ putOctal(char * cp, int len, long value)
  * Returns TRUE if the file is selected.
  */
 static BOOL
-wantFileName(const char * fileName, int fileCount, const char ** fileTable)
+wantFileName(const char * fileName, int fileCount, const char *const *fileTable)
 {
 	const char *	pathName;
 	int		fileLength;
--- sash/cmds.c
+++ sash/cmds.c
@@ -6,6 +6,7 @@
  * Most simple built-in commands are here.
  */
 
+#include "message.h"
 #include "sash.h"
 
 #include <sys/types.h>
@@ -15,12 +16,14 @@
 #include <pwd.h>
 #include <grp.h>
 #include <utime.h>
-#include <errno.h>
-#include <linux/fs.h>
+#ifndef	dev_t
+#define	dev_t	dev_t
+#endif
+#include <linux/loop.h>
 
 
 void
-do_echo(int argc, const char ** argv)
+do_echo(int argc, const char *const *argv)
 {
 	BOOL	first;
 
@@ -40,14 +43,13 @@ do_echo(int argc, const char ** argv)
 
 
 void
-do_pwd(int argc, const char ** argv)
+do_pwd(int argc, const char *const *argv)
 {
 	char	buf[PATH_LEN];
 
-	if (getcwd(buf, PATH_LEN) == NULL)
+	if ( !getcwd(buf, PATH_LEN) )
 	{
-		fprintf(stderr, "Cannot get current directory\n");
-
+		errmsg( "pwd: cannot get current directory" );
 		return;
 	}
 
@@ -56,7 +58,7 @@ do_pwd(int argc, const char ** argv)
 
 
 void
-do_cd(int argc, const char ** argv)
+do_cd(int argc, const char *const *argv)
 {
 	const char *	path;
 
@@ -66,41 +68,36 @@ do_cd(int argc, const char ** argv)
 	{
 		path = getenv("HOME");
 
-		if (path == NULL)
+		if ( !path )
 		{
-			fprintf(stderr, "No HOME environment variable\n");
-
+			errmsg( "cd: HOME not set" );
 			return;
 		}
 	}
 
 	if (chdir(path) < 0)
-		perror(path);
+		errmsg( "cd: chdir: %s: %s", path, strerror(errno) );
 }
 
 
 void
-do_mkdir(int argc, const char ** argv)
+do_mkdir(int argc, const char *const *argv)
 {
 	while (argc-- > 1)
 	{
 		if (mkdir(argv[1], 0777) < 0)
-			perror(argv[1]);
+			errmsg( "mkdir: %s: %s", argv[1], strerror(errno) );
 
-		argv++;
+		++argv;
 	}
 }
 
 
 void
-do_mknod(int argc, const char ** argv)
+do_mknod(int argc, const char *const *argv)
 {
-	const char *	cp;
-	int		mode;
-	int		major;
-	int		minor;
-
-	mode = 0666;
+	int	mode = 0666, major = 0, minor = 0;
+	const	char	*cp = argv[3];
 
 	if (strcmp(argv[2], "b") == 0)
 		mode |= S_IFBLK;
@@ -108,25 +105,19 @@ do_mknod(int argc, const char ** argv)
 		mode |= S_IFCHR;
 	else
 	{
-		fprintf(stderr, "Bad device type\n");
-
+		errmsg( "mknod: bad device type: %s", argv[2] );
 		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");
-
+		errmsg( "mknod: bad major number: %d", major );
 		return;
 	}
 
-	minor = 0;
 	cp = argv[4];
 
 	while (isDecimal(*cp))
@@ -134,51 +125,50 @@ do_mknod(int argc, const char ** argv)
 
 	if (*cp || (minor < 0) || (minor > 255))
 	{
-		fprintf(stderr, "Bad minor number\n");
-
+		errmsg( "mknod: bad minor number: %d", minor );
 		return;
 	}
 
 	if (mknod(argv[1], mode, major * 256 + minor) < 0)
-		perror(argv[1]);
+		errmsg( "mknod: %s: %s", argv[1], strerror(errno) );
 }
 
 
 void
-do_rmdir(int argc, const char ** argv)
+do_rmdir(int argc, const char *const *argv)
 {
 	while (argc-- > 1)
 	{
 		if (rmdir(argv[1]) < 0)
-			perror(argv[1]);
+			errmsg( "rmdir: %s: %s", argv[1], strerror(errno) );
 
-		argv++;
+		++argv;
 	}
 }
 
 
 void
-do_sync(int argc, const char ** argv)
+do_sync(int argc, const char *const *argv)
 {
 	sync();
 }
 
 
 void
-do_rm(int argc, const char ** argv)
+do_rm(int argc, const char *const *argv)
 {
 	while (argc-- > 1)
 	{
 		if (unlink(argv[1]) < 0)
-			perror(argv[1]);
+			errmsg( "rm: %s: %s", argv[1], strerror(errno) );
 
-		argv++;
+		++argv;
 	}
 }
 
 
 void
-do_chmod(int argc, const char ** argv)
+do_chmod(int argc, const char *const *argv)
 {
 	const char *	cp;
 	int		mode;
@@ -191,26 +181,25 @@ do_chmod(int argc, const char ** argv)
 
 	if (*cp)
 	{
-		fprintf(stderr, "Mode must be octal\n");
-
+		errmsg( "chmod: mode must be octal" );
 		return;
 	}
 
-	argc--;
-	argv++;
+	--argc;
+	++argv;
 
 	while (argc-- > 1)
 	{
 		if (chmod(argv[1], mode) < 0)
-			perror(argv[1]);
+			errmsg( "chmod: %s: %s", argv[1], strerror(errno) );
 
-		argv++;
+		++argv;
 	}
 }
 
 
 void
-do_chown(int argc, const char ** argv)
+do_chown(int argc, const char *const *argv)
 {
 	const char *	cp;
 	int		uid;
@@ -228,41 +217,39 @@ do_chown(int argc, const char ** argv)
 
 		if (*cp)
 		{
-			fprintf(stderr, "Bad uid value\n");
-
+			errmsg( "chown: bad uid value: %s", argv[1] );
 			return;
 		}
 	} else {
 		pwd = getpwnam(cp);
 
-		if (pwd == NULL)
+		if ( !pwd )
 		{
-			fprintf(stderr, "Unknown user name\n");
-
+			errmsg( "chown: unknown user name: %s", cp );
 			return;
 		}
 
 		uid = pwd->pw_uid;
 	}
 
-	argc--;
-	argv++;
+	--argc;
+	++argv;
 
 	while (argc-- > 1)
 	{
-		argv++;
+		++argv;
 
 		if ((stat(*argv, &statBuf) < 0) ||
 			(chown(*argv, uid, statBuf.st_gid) < 0))
 		{
-			perror(*argv);
+			errmsg( "chown: %s: %s", *argv, strerror(errno) );
 		}
 	}
 }
 
 
 void
-do_chgrp(int argc, const char ** argv)
+do_chgrp(int argc, const char *const *argv)
 {
 	const char *	cp;
 	int		gid;
@@ -280,8 +267,7 @@ do_chgrp(int argc, const char ** argv)
 
 		if (*cp)
 		{
-			fprintf(stderr, "Bad gid value\n");
-
+			errmsg( "chgrp: bad gid value: %s", argv[1] );
 			return;
 		}
 	}
@@ -289,34 +275,33 @@ do_chgrp(int argc, const char ** argv)
 	{
 		grp = getgrnam(cp);
 
-		if (grp == NULL)
+		if ( !grp )
 		{
-			fprintf(stderr, "Unknown group name\n");
-
+			errmsg( "chgrp: unknown group name: %s", cp );
 			return;
 		}
 
 		gid = grp->gr_gid;
 	}
 
-	argc--;
-	argv++;
+	--argc;
+	++argv;
 
 	while (argc-- > 1)
 	{
-		argv++;
+		++argv;
 
 		if ((stat(*argv, &statBuf) < 0) ||
 			(chown(*argv, statBuf.st_uid, gid) < 0))
 		{
-			perror(*argv);
+			errmsg( "chgrp: %s: %s", *argv, strerror(errno) );
 		}
 	}
 }
 
 
 void
-do_touch(int argc, const char ** argv)
+do_touch(int argc, const char *const *argv)
 {
 	const char *	name;
 	int		fd;
@@ -334,32 +319,26 @@ do_touch(int argc, const char ** argv)
 		if (fd >= 0)
 		{
 			close(fd);
-
 			continue;
 		}
 
 		if (utime(name, &now) < 0)
-			perror(name);
+			errmsg( "touch: %s: %s", name, strerror(errno) );
 	}
 }
 
 
 void
-do_mv(int argc, const char ** argv)
+do_mv(int argc, const char *const *argv)
 {
-	const char *	srcName;
-	const char *	destName;
-	const char *	lastArg;
-	BOOL		dirFlag;
-
-	lastArg = argv[argc - 1];
-
-	dirFlag = isDirectory(lastArg);
+	const	char	*srcName;
+	const	char	*destName;
+	const	char	*lastArg = argv[argc - 1];
+	BOOL	dirFlag = isDirectory(lastArg);
 
 	if ((argc > 3) && !dirFlag)
 	{
-		fprintf(stderr, "%s: not a directory\n", lastArg);
-
+		errmsg( "mv: %s: when moving multiple files, last argument must be a directory", lastArg );
 		return;
 	}
 
@@ -369,8 +348,7 @@ do_mv(int argc, const char ** argv)
 
 		if (access(srcName, 0) < 0)
 		{
-			perror(srcName);
-
+			errmsg( "mv: %s: %s", srcName, strerror(errno) );
 			continue;
 		}
 
@@ -384,8 +362,7 @@ do_mv(int argc, const char ** argv)
 
 		if (errno != EXDEV)
 		{
-			perror(destName);
-
+			errmsg( "mv: %s: %s", destName, strerror(errno) );
 			continue;
 		}
 
@@ -393,65 +370,45 @@ do_mv(int argc, const char ** argv)
 			continue;
 
 		if (unlink(srcName) < 0)
-			perror(srcName);
+			errmsg( "mv: error unlinking %s: %s", srcName, strerror(errno) );
 	}
 }
 
 
 void
-do_ln(int argc, const char ** argv)
+do_ln(int argc, const char *const *argv)
 {
-	const char *	srcName;
-	const char *	destName;
-	const char *	lastArg;
-	BOOL		dirFlag;
+	const	char *	srcName;
+	const	char *	destName;
+	const	char *	lastArg = argv[argc - 1];
+	BOOL		dirFlag = isDirectory(lastArg), sym = FALSE;
 
 	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");
-
+			errmsg( "ln: unknown option: %s", argv[1] );
 			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;
+		sym = TRUE;
+		--argc;
+		++argv;
 	}
 
-	/*
-	 * Here for normal hard links.
-	 */
-	lastArg = argv[argc - 1];
-	dirFlag = isDirectory(lastArg);
-
 	if ((argc > 3) && !dirFlag)
 	{
-		fprintf(stderr, "%s: not a directory\n", lastArg);
-
+		errmsg( "ln: %s: when making multiple links, last argument must be a directory", lastArg );
 		return;
 	}
 
-	while (argc-- > 2)
+	while (--argc > 1)
 	{
 		srcName = *(++argv);
 
-		if (access(srcName, 0) < 0)
+		if ( !sym && access(srcName, 0) < 0)
 		{
-			perror(srcName);
-
+			errmsg( "ln: %s: %s", srcName, strerror(errno) );
 			continue;
 		}
 
@@ -460,18 +417,15 @@ do_ln(int argc, const char ** argv)
 		if (dirFlag)
 			destName = buildName(destName, srcName);
 
-		if (link(srcName, destName) < 0)
-		{
-			perror(destName);
-
-			continue;
-		}
+		if ( (sym ? symlink(srcName, destName) : link(srcName, destName)) < 0 )
+			errmsg( "%s: %s -> %s: %s", (sym ? "symlink" : "ln"),
+				srcName, destName, strerror(errno) );
 	}
 }
 
 
 void
-do_cp(int argc, const char ** argv)
+do_cp(int argc, const char *const *argv)
 {
 	const char *	srcName;
 	const char *	destName;
@@ -484,8 +438,7 @@ do_cp(int argc, const char ** argv)
 
 	if ((argc > 3) && !dirFlag)
 	{
-		fprintf(stderr, "%s: not a directory\n", lastArg);
-
+		errmsg( "cp: %s: when copying multiple files, last argument must be a directory", lastArg );
 		return;
 	}
 
@@ -503,20 +456,20 @@ do_cp(int argc, const char ** argv)
 
 
 void
-do_mount(int argc, const char ** argv)
+do_mount(int argc, const char *const *argv)
 {
 	const char *	str;
 	const char *	type;
 	int		flags;
 
-	argc--;
-	argv++;
+	--argc;
+	++argv;
 	type = "ext2";
 	flags = MS_MGC_VAL;
 
 	while ((argc > 0) && (**argv == '-'))
 	{
-		argc--;
+		--argc;
 		str = *argv++;
 
 		while (*++str) switch (*str)
@@ -524,13 +477,12 @@ do_mount(int argc, const char ** argv)
 			case 't':
 				if ((argc <= 0) || (**argv == '-'))
 				{
-					fprintf(stderr, "Missing file system type\n");
-
+					errmsg( "mount: missing file system type" );
 					return;
 				}
 
 				type = *argv++;
-				argc--;
+				--argc;
 				break;
 
 			case 'r':
@@ -542,34 +494,33 @@ do_mount(int argc, const char ** argv)
 				break;
 
 			default:
-				fprintf(stderr, "Unknown option\n");
-
+				errmsg( "mount: unknown option: %s", str );
 				return;
 		}
 	}
 
 	if (argc != 2)
 	{
-		fprintf(stderr, "Wrong number of arguments for mount\n");
+		errmsg( "mount: wrong number of arguments for mount" );
 
 		return;
 	}
 
 	if (mount(argv[0], argv[1], type, flags, 0) < 0)
-		perror("mount failed");
+		errmsg( "mount: %s", strerror(errno) );
 }
 
 
 void
-do_umount(int argc, const char ** argv)
+do_umount(int argc, const char *const *argv)
 {
 	if (umount(argv[1]) < 0)
-		perror(argv[1]);
+		errmsg( "umount: %s: %s", argv[1], strerror(errno) );
 }
 
 
 void
-do_cmp(int argc, const char ** argv)
+do_cmp(int argc, const char *const *argv)
 {
 	int		fd1;
 	int		fd2;
@@ -585,30 +536,26 @@ do_cmp(int argc, const char ** argv)
 
 	if (stat(argv[1], &statBuf1) < 0)
 	{
-		perror(argv[1]);
-
+		errmsg( "cmp: %s: %s", argv[1], strerror(errno) );
 		return;
 	}
 
 	if (stat(argv[2], &statBuf2) < 0)
 	{
-		perror(argv[2]);
-
+		errmsg( "cmp: %s: %s", argv[2], strerror(errno) );
 		return;
 	}
 
 	if ((statBuf1.st_dev == statBuf2.st_dev) &&
 		(statBuf1.st_ino == statBuf2.st_ino))
 	{
-		printf("Files are links to each other\n");
-
+		puts( "Files are links to each other" );
 		return;
 	}
 
 	if (statBuf1.st_size != statBuf2.st_size)
 	{
-		printf("Files are different sizes\n");
-
+		puts( "Files are different sizes" );
 		return;
 	}
 
@@ -616,8 +563,7 @@ do_cmp(int argc, const char ** argv)
 
 	if (fd1 < 0)
 	{
-		perror(argv[1]);
-
+		errmsg( "cmp: error opening %s: %s", argv[1], strerror(errno) );
 		return;
 	}
 
@@ -625,15 +571,14 @@ do_cmp(int argc, const char ** argv)
 
 	if (fd2 < 0)
 	{
-		perror(argv[2]);
+		errmsg( "cmp: error opening %s: %s", argv[2], strerror(errno) );
 		close(fd1);
-
 		return;
 	}
 
 	pos = 0;
 
-	while (TRUE)
+	for(;;)
 	{
 		if (intFlag)
 			goto closefiles;
@@ -642,7 +587,7 @@ do_cmp(int argc, const char ** argv)
 
 		if (cc1 < 0)
 		{
-			perror(argv[1]);
+			errmsg( "cmp: error reading %s: %s", argv[1], strerror(errno) );
 			goto closefiles;
 		}
 
@@ -650,25 +595,25 @@ do_cmp(int argc, const char ** argv)
 
 		if (cc2 < 0)
 		{
-			perror(argv[2]);
+			errmsg( "cmp: error reading %s: %s", argv[2], strerror(errno) );
 			goto closefiles;
 		}
 
-		if ((cc1 == 0) && (cc2 == 0))
+		if ( !cc1 && !cc2 )
 		{
-			printf("Files are identical\n");
+			puts( "Files are identical" );
 			goto closefiles;
 		}
 
 		if (cc1 < cc2)
 		{
-			printf("First file is shorter than second\n");
+			puts( "First file is shorter than second" );
 			goto closefiles;
 		}
 
 		if (cc1 > cc2)
 		{
-			printf("Second file is shorter than first\n");
+			puts( "Second file is shorter than first" );
 			goto closefiles;
 		}
 
@@ -685,7 +630,7 @@ do_cmp(int argc, const char ** argv)
 		while (*bp1++ == *bp2++)
 			pos++;
 
-		printf("Files differ at byte position %ld\n", pos);
+		printf( "Files differ at byte position %ld\n", pos );
 
 		goto closefiles;
 	}
@@ -697,7 +642,7 @@ closefiles:
 
 
 void
-do_more(int argc, const char ** argv)
+do_more(int argc, const char *const *argv)
 {
 	FILE *		fp;
 	const char *	name;
@@ -739,11 +684,9 @@ do_more(int argc, const char ** argv)
 		name = *(++argv);
 
 		fp = fopen(name, "r");
-
-		if (fp == NULL)
+		if ( !fp )
 		{
-			perror(name);
-
+			errmsg( "more: error opening %s: %s", name, strerror(errno) );
 			return;
 		}
 
@@ -833,7 +776,7 @@ do_more(int argc, const char ** argv)
 
 
 void
-do_sum(int argc, const char ** argv)
+do_sum(int argc, const char *const *argv)
 {
 	const char *	name;
 	int		fd;
@@ -843,8 +786,8 @@ do_sum(int argc, const char ** argv)
 	unsigned long	checksum;
 	char		buf[BUF_SIZE];
 
-	argc--;
-	argv++;
+	--argc;
+	++argv;
 
 	while (argc-- > 0)
 	{
@@ -854,8 +797,7 @@ do_sum(int argc, const char ** argv)
 
 		if (fd < 0)
 		{
-			perror(name);
-
+			errmsg( "sum: error opening %s: %s", name, strerror(errno) );
 			continue;
 		}
 
@@ -878,8 +820,7 @@ do_sum(int argc, const char ** argv)
 
 		if (cc < 0)
 		{
-			perror(name);
-
+			errmsg( "sum: error reading %s: %s", name, strerror(errno) );
 			(void) close(fd);
 
 			continue;
@@ -893,58 +834,34 @@ do_sum(int argc, const char ** argv)
 
 
 void
-do_exit(int argc, const char ** argv)
+do_exit(int argc, const char *const *argv)
 {
-	if (getpid() == 1)
+	if ( getpid() == 1 )
 	{
-		fprintf(stderr, "You are the INIT process!\n");
-
+		errmsg( "exit: you are the INIT process!" );
 		return;
 	}
 
-	exit(0);
+	exit( argc ? atoi( argv[1] ) : 0 );
 }
 
 
 void
-do_setenv(int argc, const char ** argv)
+do_setenv(int argc, const char *const *argv)
 {
-	const char *	name;
-	const char *	value;
-	char *		str;
-
-	name = argv[1];
-	value = argv[2];
-
-	/*
-	 * The value given to putenv must remain around, so we must malloc it.
-	 * Note: memory is not reclaimed if the same variable is redefined.
-	 */
-	str = malloc(strlen(name) + strlen(value) + 2);
-
-	if (str == NULL)
-	{
-		fprintf(stderr, "Cannot allocate memory\n");
-
-		return;
-	}
-
-	strcpy(str, name);
-	strcat(str, "=");
-	strcat(str, value);
-
-	putenv(str);
+	if ( setenv( argv[1], argv[2], 1 ) < 0 )
+		errmsg( "setenv: %s", strerror(errno) );
 }
 
 
 void
-do_printenv(int argc, const char ** argv)
+do_printenv(int argc, const char *const *argv)
 {
-	const char **	env;
+	const char *const *	env;
 	extern char **	environ;
 	int		len;
 
-	env = (const char **) environ;
+	env = (const char *const *) environ;
 
 	if (argc == 1)
 	{
@@ -971,7 +888,7 @@ do_printenv(int argc, const char ** argv)
 
 
 void
-do_umask(int argc, const char ** argv)
+do_umask(int argc, const char *const *argv)
 {
 	const char *	cp;
 	int		mask;
@@ -993,63 +910,91 @@ do_umask(int argc, const char ** argv)
 
 	if (*cp || (mask & ~0777))
 	{
-		fprintf(stderr, "Bad umask value\n");
-
+		errmsg( "umask: bad value: %o", mask );
 		return;
 	}
 
 	umask(mask);
 }
 
+typedef struct
+{
+	int	no;
+	const char *name;
+} sig_desc_t;
+
+static sig_desc_t	sig_desc[] = {
+	{ SIGHUP, "HUP" },
+	{ SIGINT, "INT" },
+	{ SIGQUIT, "QUIT" },
+	{ SIGILL, "ILL" },
+	{ SIGTRAP, "TRAP" },
+	{ SIGABRT, "ABRT" },
+	{ SIGIOT, "IOT" },
+	{ SIGBUS, "BUS" },
+	{ SIGFPE, "FPE" },
+	{ SIGKILL, "KILL" },
+	{ SIGUSR1, "USR1" },
+	{ SIGSEGV, "SEGV" },
+	{ SIGUSR2, "USR2" },
+	{ SIGPIPE, "PIPE" },
+	{ SIGALRM, "ALRM" },
+	{ SIGTERM, "TERM" },
+#ifdef SIGSTKFLT
+	{ SIGSTKFLT, "STKFLT" },
+#endif
+	{ SIGSYS, "SYS" },
+	{ SIGCHLD, "CHLD" },
+	{ SIGCONT, "CONT" },
+	{ SIGSTOP, "STOP" },
+	{ SIGTSTP, "TSTP" },
+	{ SIGTTIN, "TTIN" },
+	{ SIGTTOU, "TTOU" },
+	{ SIGURG, "URG" },
+	{ SIGXCPU, "XCPU" },
+	{ SIGXFSZ, "XFSZ" },
+	{ SIGVTALRM, "VTALRM" },
+	{ SIGPROF, "PROF" },
+	{ SIGWINCH, "WINCH" },
+	{ SIGIO, "IO" },
+	{ SIGPOLL, "POLL" },
+	{ SIGPWR, "PWR" }
+};
 
 void
-do_kill(int argc, const char ** argv)
+do_kill(int argc, const char *const *argv)
 {
 	const char *	cp;
-	int		sig;
-	int		pid;
+	int		sig = SIGTERM, pid;
 
-	sig = SIGTERM;
-
-	if (argv[1][0] == '-')
+	if ( *argv[1] == '-' )
 	{
+		unsigned	i = 0;
+		sig = 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, "KILL") == 0)
-			sig = SIGKILL;
-		else if (strcmp(cp, "STOP") == 0)
-			sig = SIGSTOP;
-		else if (strcmp(cp, "CONT") == 0)
-			sig = SIGCONT;
-		else if (strcmp(cp, "USR1") == 0)
-			sig = SIGUSR1;
-		else if (strcmp(cp, "USR2") == 0)
-			sig = SIGUSR2;
-		else if (strcmp(cp, "TERM") == 0)
-			sig = SIGTERM;
-		else
+		for ( ; i < sizeof(sig_desc) / sizeof(*sig_desc); ++i )
 		{
-			sig = 0;
+			if ( !strcmp( cp, sig_desc[i].name ) )
+			{
+				sig = sig_desc[i].no;
+				break;
+			}
+		}
 
+		if ( !sig )
+		{
 			while (isDecimal(*cp))
 				sig = sig * 10 + *cp++ - '0';
 
-			if (*cp)
+			if ( sig <= 0 || sig > 31 || *cp )
 			{
-				fprintf(stderr, "Unknown signal\n");
-
+				errmsg( "kill: unknown signal: %s", &argv[1][1] );
 				return;
 			}
 		}
 
-		argc--;
-		argv++;
+		--argc;
+		++argv;
 	}
 
 	while (argc-- > 1)
@@ -1062,19 +1004,18 @@ do_kill(int argc, const char ** argv)
 
 		if (*cp)
 		{
-			fprintf(stderr, "Non-numeric pid\n");
-
+			errmsg( "kill: non-numeric pid: %s", *argv );
 			return;
 		}
 
 		if (kill(pid, sig) < 0)
-			perror(*argv);
+			errmsg( "kill: %d: %s", pid, strerror(errno) );
 	}
 }
 
 
 void
-do_where(int argc, const char ** argv)
+do_where(int argc, const char *const *argv)
 {
 	const char *	program;
 	const char *	dirName;
@@ -1086,10 +1027,9 @@ do_where(int argc, const char ** argv)
 	found = FALSE;
 	program = argv[1];
 
-	if (strchr(program, '/') != NULL)
+	if ( strchr(program, '/') )
 	{
-		fprintf(stderr, "Program name cannot include a path\n");
-
+		errmsg( "where: program name cannot include a path" );
 		return;
 	}
 
@@ -1098,10 +1038,9 @@ do_where(int argc, const char ** argv)
 	fullPath = getChunk(strlen(path) + strlen(program) + 2);
 	path = chunkstrdup(path);
 
-	if ((path == NULL) || (fullPath == NULL))
+	if ( !path || !fullPath )
 	{
-		fprintf(stderr, "Memory allocation failed\n");
-
+		errmsg( "where: memory allocation failure: %s", strerror(errno) );
 		return;
 	}
 
@@ -1155,4 +1094,64 @@ do_where(int argc, const char ** argv)
 		printf("Program \"%s\" not found in PATH\n", program);
 }
 
+void	do_losetup(int argc, const char *const *argv)
+{
+	int loopfd, targfd;
+	struct loop_info loopInfo;
+
+	if ( !strcmp( argv[1], "-d" ) )
+	{
+		loopfd = open( argv[2], O_RDONLY );
+		if ( loopfd < 0 )
+		{
+			errmsg( "losetup: error opening %s: %s", argv[2], strerror(errno) );
+			return;
+		}
+
+		if ( ioctl(loopfd, LOOP_CLR_FD, 0) < 0 )
+		{
+			errmsg( "losetup: error unassociating device: %s", strerror(errno) );
+			close( loopfd );
+			return;
+		}
+
+		close( loopfd );
+		return;
+	}
+
+	loopfd = open( argv[1], O_RDONLY );
+	if ( loopfd < 0 )
+	{
+		errmsg( "losetup: error opening %s: %s", argv[1], strerror(errno) );
+		return;
+	}
+
+	targfd = open( argv[2], O_RDONLY );
+	if ( targfd < 0 )
+	{
+		errmsg( "losetup: error opening %s: %s", argv[2], strerror(errno) );
+		close( loopfd );
+		return;
+	}
+
+	if ( ioctl(loopfd, LOOP_SET_FD, targfd) < 0 )
+	{
+		errmsg( "losetup: error setting up loopback device: %s", strerror(errno) );
+		close( loopfd );
+		close( targfd );
+		return;
+	}
+
+	memset(&loopInfo, 0, sizeof(loopInfo));
+	strcpy(loopInfo.lo_name, argv[2]);
+
+	if ( ioctl(loopfd, LOOP_SET_STATUS, &loopInfo) < 0 )
+		errmsg( "losetup: error setting up loopback device: %s", strerror(errno) );
+
+	close( loopfd );
+	close( targfd );
+
+	return;
+}
+
 /* END CODE */
--- /dev/null
+++ sash/getline.c
@@ -0,0 +1,75 @@
+#include <errno.h>
+#include "getline.h"
+#include "sash.h"
+
+int	rl_disable;
+
+static	void	showPrompt( void )
+{
+	const char *cp = prompt ?: "$ ";
+	write( STDOUT_FILENO, cp, strlen(cp) );
+}	
+
+static	char	*get_line_norl( FILE *fp )
+{
+	static	char	buf[CMD_LEN];
+	int	is_tty = isatty(fileno(fp));
+	char	*p;
+
+	if ( is_tty )
+		showPrompt();
+
+	for(;;)
+	{
+		if ( fgets( buf, CMD_LEN - 1, fp ) )
+			break;
+
+		if ( ferror(fp) && (EINTR == errno) )
+		{
+			clearerr( fp );
+			continue;
+		}
+
+		return 0;
+	}
+
+	for ( p = buf + strlen(buf) - 1; p >= buf; --p )
+		if ( !isspace( *p ) )
+			break;
+
+	p[1] = '\0';
+
+	return buf;
+}
+
+#ifdef	WITH_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+
+static	char	*get_line_rl( FILE *fp )
+{
+	static	char	*input;
+
+	if ( input )
+		free( input );
+
+	intCrlf = FALSE;
+	input = readline( prompt ?: "$ " );
+	intCrlf = TRUE;
+
+	if ( input && *input )
+		add_history( input );
+
+	return input;
+}
+#endif	/* WITH_READLINE */
+
+char	*get_line( FILE *fp )
+{
+#ifdef	WITH_READLINE
+	if ( !rl_disable && isatty(fileno(fp)) )
+		return get_line_rl( fp );
+	else
+#endif	/* WITH_READLINE */
+		return get_line_norl( fp );
+}
--- /dev/null
+++ sash/getline.h
@@ -0,0 +1,12 @@
+#ifndef	__GETLINE_H__
+#define	__GETLINE_H__
+
+#include <stdio.h>
+
+extern	int	intCrlf;
+extern	int	rl_disable;
+extern	char	*prompt;
+
+extern	char	*get_line( FILE *fp );
+
+#endif	/* __GETLINE_H__ */
--- /dev/null
+++ sash/message.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "message.h"
+
+extern const char *__progname;
+
+static	void	print_message( const char *fmt, va_list args )
+{
+	fprintf( stderr, "%s: ", __progname );
+	vfprintf( stderr, fmt, args );
+	fputs( "\r\n", stderr );
+	fflush( stderr );
+}
+
+void errmsg( const char *fmt, ... )
+{
+	va_list args;
+
+	va_start( args, fmt );
+	print_message( fmt, args );
+	va_end( args );
+}
+
+void fatal( const char *fmt, ... )
+{
+	va_list args;
+
+	va_start( args, fmt );
+	print_message( fmt, args );
+	va_end( args );
+	exit( 1 );
+}
--- /dev/null
+++ sash/message.h
@@ -0,0 +1,7 @@
+#ifndef	__MESSAGE_H__
+#define	__MESSAGE_H__
+
+void errmsg( const char *fmt, ... ) __attribute__ ((format (printf, 1, 2)));
+void fatal( const char *fmt, ... ) __attribute__ ((noreturn)) __attribute__ ((format (printf, 1, 2)));
+
+#endif	/* __MESSAGE_H__ */
--- sash/sash.1
+++ sash/sash.1
@@ -2,7 +2,7 @@
 .SH NAME
 sash \- stand-alone shell with built-in commands
 .SH SYNOPSYS
-.B sash [-c command] [-p prompt] [-q] [-a]
+.B sash [-c command] [-p prompt] [-q] [-a] [script]
 .SH DESCRIPTION
 The
 .B sash
@@ -24,9 +24,9 @@ These built-in commands are:
 .nf
      -ar, -chattr, -chgrp, -chmod, -chown, -cmp, -cp,
      -dd, -echo, -ed, -grep, -file, -find, -gunzip,
-     -gzip, -kill, -ln, -ls, -lsattr, -mkdir, -mknod,
-     -more, -mount, -mv, -printenv, -pwd, -rm, -rmdir,
-     -sum, -sync, -tar, -touch, -umount, -where
+     -gzip, -kill, -losetup, -ln, -ls, -lsattr, -mkdir,
+     -mknod, -more, -mount, -mv, -printenv, -pwd, -rm,
+     -rmdir, -sum, -sync, -tar, -touch, -umount, -where
 .fi
 .PP
 These commands are generally similar to the standard programs with similar
@@ -303,6 +303,12 @@ is a numeric value, or one of the special values HUP, INT,
 QUIT, KILL, TERM, STOP, CONT, USR1 or USR2.
 If no signal is specified then SIGTERM is used.
 .TP
+.B -losetup [-d] loopDev [file]
+Associates loopback devices with files on the system. If -d is not given,
+the loopback device \fBloopDev\fR is associated with \fBfile\fR. If -d is
+given, \fBloopDev\fR is unassociated with the file it's currently configured
+for.
+.TP
 .B -ln [-s] srcName ... destName
 Links one or more files from the
 .I srcName
@@ -455,20 +461,36 @@ then the reason is also printed.
 .SH OPTIONS
 There are several command line options to
 .BR sash .
-The -c option executes the next argument as a command (including embedded
+.TP
+.B -c
+Executes the next argument as a command (including embedded
 spaces to separate the arguments of the command), and then exits.
-.PP
-The -p option takes the next argument as the prompt string to be used
+.TP
+.B -p
+Takes the next argument as the prompt string to be used
 when prompting for commands.
-.PP
-The -q option makes
+.TP
+.B -q
+Makes
 .B sash
 quiet, which simply means that it doesn't print its introduction line
 when it starts.
-.PP
-The -a option creates aliases for the built-in commands so
+.TP
+.B -a
+Creates aliases for the built-in commands so
 that they replace the corresponding standard commands.
 This is the same result as if the 'aliasall' command was used.
+.TP
+.B -e
+Disables command editing and history support, if it was compiled into the
+.B sash
+executable. Otherwise, does nothing.
+.PP
+A file name may be provided as the last argument to sash, in which case
+sash's standard input is read from that file. This allows #! scripts
+to use sash as their script interpretor. Be aware that sash does not provide
+most normal bourne-shell programming features, however.
+
 .SH SYSTEM RECOVERY
 This section contains some useful information about using
 .B sash
--- sash/sash.c
+++ sash/sash.c
@@ -10,8 +10,9 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <signal.h>
-#include <errno.h>
 
+#include "getline.h"
+#include "message.h"
 #include "sash.h"
 
 
@@ -31,7 +32,7 @@ static const char * const	version = "3.4";
 typedef struct
 {
 	const char *	name;
-	void		(*func)(int argc, const char ** argv);
+	void		(*func)(int argc, const char *const *argv);
 	int		minArgs;
 	int		maxArgs;
 	const char *	description;
@@ -132,9 +133,9 @@ static const CommandEntry	commandEntryTable[] =
 	},
 
 	{
-		"exit",		do_exit,	1,	1,
+		"exit",		do_exit,	1,	2,
 		"Exit from sash",
-		""
+		"[exitcode]"
 	},
 
 	{
@@ -182,6 +183,12 @@ static const CommandEntry	commandEntryTable[] =
 	},
 
 	{
+		"-losetup",	do_losetup,	3,	3,
+		"Associate a loopback device with a file",
+		"[-d] device\n       -losetup device filename"
+	},
+
+	{
 		"-ln",		do_ln,		3,	INFINITE_ARGS,
 		"Link one fileName to another",
 		"[-s] srcName ... destName"
@@ -351,25 +358,21 @@ typedef struct
 static	Alias *	aliasTable;
 static	int	aliasCount;
 
-static	FILE *	sourcefiles[MAX_SOURCE];
-static	int	sourceCount;
-
-static	BOOL	intCrlf = TRUE;
-static	char *	prompt;
+int	intCrlf = TRUE;
+char	*prompt;
 
 
 /*
  * Local procedures.
  */
-static	void	catchInt(int);
-static	void	catchQuit(int);
-static	void	readFile(const char * name);
+static	void	catchInterrupt(int);
+static	int	readFile(const char * name);
 static	void	command(const char * cmd);
 static	BOOL	tryBuiltIn(const char * cmd);
 static	void	runCmd(const char * cmd);
-static	void	childProcess(const char * cmd);
-static	void	showPrompt(void);
-static	void	usage(void);
+static	void	childProcess(const char * cmd) __attribute__ ((noreturn));
+static	void	print_version( FILE *fp );
+static	void	usage(void) __attribute__ ((noreturn));
 static	Alias *	findAlias(const char * name);
 
 
@@ -378,143 +381,128 @@ static	Alias *	findAlias(const char * name);
  */
 BOOL	intFlag;
 
-
-int
-main(int argc, const char ** argv)
+int	main( int argc, char *const argv[] )
 {
-	const char *	cp;
-	const char *	singleCommand;
-	BOOL		quietFlag;
-	BOOL		aliasFlag;
-	char		buf[PATH_LEN];
-
-	singleCommand = NULL;
-	quietFlag = FALSE;
-	aliasFlag = FALSE;
-
-	/*
-	 * Look for options.
-	 */
-	argv++;
-	argc--;
+	const	char	*singleCommand = NULL;
+	BOOL	aliasFlag = FALSE, quietFlag = FALSE;
+	int	ch;
+	char	*cp;
 
-	while ((argc > 0) && (**argv == '-'))
+	while ( (ch = getopt( argc, argv, "aeqh?c:p:" )) >= 0 )
 	{
-		cp = *argv++ + 1;
-		argc--;
-
-		while (*cp) switch (*cp++)
+		switch ( ch )
 		{
-			case 'c':
-				/*
-				 * Execute specified command.
-				 */
-				if ((argc != 1) || singleCommand)
-					usage();
-
-				singleCommand = *argv++;
-				argc--;
-
+			case 'a':
+				aliasFlag = TRUE;
 				break;
-
-			case 'p':
-				/*
-				 * Set the prompt string.
-				 */
-				if ((argc <= 0) || (**argv == '-'))
-					usage();
-
-				if (prompt)
-					free(prompt);
-
-				prompt = strdup(*argv++);
-				argc--;
-
+			case 'e':
+				rl_disable = TRUE;
 				break;
-
 			case 'q':
 				quietFlag = TRUE;
 				break;
-
-			case 'a':
-				aliasFlag = TRUE;
+			case 'c':
+				if ( singleCommand )
+					usage();
+				singleCommand = strdup( optarg );
+				if ( !singleCommand )
+					fatal( "error during initialization: %s", strerror(errno) );
+				break;
+			case 'p':
+				if ( prompt )
+					free( prompt );
+	
+				prompt = strdup( optarg );
+				if ( !prompt )
+					fatal( "error during initialization: %s", strerror(errno) );
 				break;
-
 			case 'h':
 			case '?':
 				usage();
-				break;
-
 			default:
-				fprintf(stderr, "Unknown option -%c\n", cp[-1]);
-
-				return 1;
+				fatal( "unrecognized option: -%c", ch );
 		}
 	}
 
 	/*
-	 * No more arguments are allowed.
+	 * No arguments are allowed for single command.
 	 */
-	if (argc > 0)
+	if ( (singleCommand && optind < argc) || (optind + 1 < argc) )
 		usage();
 
 	/*
 	 * Default our path if it is not set.
 	 */
-	if (getenv("PATH") == NULL)
-		putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc");
+	if ( !getenv("PATH") )
+		putenv( "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc" );
 
 	/*
 	 * If the alias flag is set then define all aliases.
 	 */
-	if (aliasFlag)
-		do_aliasall(0, NULL);
+	if ( aliasFlag )
+		do_aliasall( 0, NULL );
 
 	/*
 	 * If we are to execute a single command, then do so and exit.
 	 */
-	if (singleCommand)
+	if ( singleCommand )
 	{
-		command(singleCommand);
-
+		command( singleCommand );
 		return 0;
 	}
 
+	if ( optind < argc )
+	{
+		int fd = open( argv[optind], O_RDONLY );
+		if ( fd < 0 )
+			fatal( "error opening %s: %s", argv[optind], strerror(errno) );
+
+		if ( dup2(fd, STDIN_FILENO) < 0 )
+			fatal( "dup2: %s", strerror(errno) );
+
+		if ( fcntl( STDIN_FILENO, F_SETFD, FD_CLOEXEC ) < 0 )
+			fatal( "fcntl: %s", strerror(errno) );
+
+		if ( close(fd) < 0 )
+			fatal( "close: %s", strerror(errno) );
+	}
+
 	/*
 	 * Print a hello message unless we are told to be silent.
 	 */
-	if (!quietFlag && isatty(STDIN))
+	if ( !quietFlag && isatty(STDIN_FILENO) )
 	{
-		printf("Stand-alone shell (version %s)\n", version);
+		print_version( stdout );
 
 		if (aliasFlag)
 			printf("Built-in commands are aliased to standard commands\n");
 	}
 
-	signal(SIGINT, catchInt);
-	signal(SIGQUIT, catchQuit);
+	signal(SIGINT, catchInterrupt);
+	signal(SIGQUIT, catchInterrupt);
 
 	/*
 	 * Execute the user's alias file if present.
 	 */
 	cp = getenv("HOME");
 
-	if (cp)
+	if ( cp )
 	{
-		strcpy(buf, cp);
-		strcat(buf, "/");
-		strcat(buf, ".aliasrc");
+		const	char	aliasrc[] = "/.aliasrc";
+		unsigned	len = strlen( cp );
+		char	fname[len + sizeof(aliasrc)];
+
+		memcpy( fname, cp, len );
+		memcpy( fname + len, aliasrc, sizeof(aliasrc) );
 
-		if ((access(buf, 0) == 0) || (errno != ENOENT))
-			readFile(buf);
+		if ( !access(fname, R_OK ) )
+			readFile( fname );
 	}
 
 	/*
 	 * Read commands from stdin.
 	 */
-	readFile(NULL);
-
-	return 0;
+	return readFile( 0 );
 }
 
 
@@ -522,80 +510,55 @@ main(int argc, const char ** argv)
  * Read commands from the specified file.
  * A null name pointer indicates to read from stdin.
  */
-static void
+static int
 readFile(const char * name)
 {
-	FILE *	fp;
-	int	cc;
-	BOOL	ttyFlag;
-	char	buf[CMD_LEN];
-
-	if (sourceCount >= MAX_SOURCE)
-	{
-		fprintf(stderr, "Too many source files\n");
-
-		return;
-	}
-
-	fp = stdin;
+	FILE	*fp = stdin;
 
-	if (name)
+	if ( name )
 	{
-		fp = fopen(name, "r");
-
-		if (fp == NULL)
+		fp = fopen( name, "r" );
+		if ( !fp )
 		{
-			perror(name);
+			errmsg( "%s: %s", name, strerror(errno) );
+			return 1;
+		}
 
-			return;
+		if ( fcntl( fileno(fp), F_SETFD, FD_CLOEXEC ) < 0 )
+		{
+			errmsg( "fcntl: %s: %s", name, strerror(errno) );
+			return 1;
 		}
 	}
 
-	sourcefiles[sourceCount++] = fp;
-
-	ttyFlag = isatty(fileno(fp));
-
-	while (TRUE)
+	for(;;)
 	{
-		if (ttyFlag)
-			showPrompt();
+		const char *input;
 
-		if (intFlag && !ttyFlag && (fp != stdin))
+		if ( intFlag && !isatty(fileno(fp)) )
 		{
-			fclose(fp);
-			sourceCount--;
+			if (fp != stdin)
+				fclose(fp);
 
-			return;
+			return 128;
 		}
-	
-		if (fgets(buf, CMD_LEN - 1, fp) == NULL)
-		{
-			if (ferror(fp) && (errno == EINTR))
-			{
-				clearerr(fp);
 
-				continue;
-			}
+		input = get_line( fp );
 
-			break;
+		if ( !input )
+		{
+			if ( getpid() == 1 )
+				continue;
+			else
+				break;
 		}
 
-		cc = strlen(buf);
-
-		if (buf[cc - 1] == '\n')
-			cc--;
-
-		while ((cc > 0) && isBlank(buf[cc - 1]))
-			cc--;
-
-		buf[cc] = '\0';
-
-		command(buf);
+		command( input );
 	}
 
-	if (ferror(fp))
+	if ( ferror(fp) )
 	{
-		perror("Reading command line");
+		errmsg( "error reading command input: %s", strerror(errno) );
 
 		if (fp == stdin)
 			exit(1);
@@ -606,7 +569,7 @@ readFile(const char * name)
 	if (fp != stdin)
 		fclose(fp);
 
-	sourceCount--;
+	return 0;
 }
 
 
@@ -695,45 +658,43 @@ command(const char * cmd)
 static BOOL
 tryBuiltIn(const char * cmd)
 {
-	const char *		endCmd;
-	const CommandEntry *	entry;
-	int			argc;
-	const char **		argv;
-	char			cmdName[CMD_LEN];
+	char	*cmdName;
+	const	char	*endCmd = cmd;
+	const	CommandEntry	*entry;
+	int	argc;
+	const char	*const	*argv;
 
 	/*
 	 * Look for the end of the command name and then copy the
 	 * command name to a buffer so we can null terminate it.
 	 */
-	endCmd = cmd;
-
-	while (*endCmd && !isBlank(*endCmd))
-		endCmd++;
+	while ( *endCmd && !isBlank(*endCmd) )
+		++endCmd;
 
+	cmdName = alloca( endCmd - cmd + 1 );
 	memcpy(cmdName, cmd, endCmd - cmd);
-
 	cmdName[endCmd - cmd] = '\0';
 
 	/*
 	 * Search the command table looking for the command name.
 	 */
-	for (entry = commandEntryTable; entry->name != NULL; entry++)
+	for ( entry = commandEntryTable; entry->name; ++entry )
 	{
-		if (strcmp(entry->name, cmdName) == 0)
+		if ( !strcmp(entry->name, cmdName) )
 			break;
 	}
 
 	/*
 	 * If the command is not a built-in, return indicating that.
 	 */
-	if (entry->name == NULL)
+	if ( !entry->name )
 		return FALSE;
 
 	/*
 	 * The command is a built-in.
 	 * Break the command up into arguments and expand wildcards.
 	 */
-	if (!makeArgs(cmd, &argc, &argv))
+	if ( !makeArgs(cmd, &argc, &argv) )
 		return TRUE;
 
 	/*
@@ -742,7 +703,7 @@ tryBuiltIn(const char * cmd)
 	 */
 	if ((argc < entry->minArgs) || (argc > entry->maxArgs))
 	{
-		fprintf(stderr, "usage: %s %s\n", entry->name, entry->usage);
+		errmsg( "usage: %s %s", entry->name, entry->usage);
 
 		return TRUE;
 	}
@@ -818,7 +779,7 @@ runCmd(const char * cmd)
 
 	if (pid < 0)
 	{
-		perror("fork failed");
+		errmsg( "fork failed: %s", strerror(errno) );
 
 		return;
 	}
@@ -841,18 +802,15 @@ runCmd(const char * cmd)
 
 	intCrlf = TRUE;
 
-	if (pid < 0)
+	if ( pid < 0 )
 	{
-		fprintf(stderr, "Error from waitpid: %s", strerror(errno));
-
+		errmsg( "waitpid: %s", strerror(errno) );
 		return;
 	}
 
-	if (WIFSIGNALED(status))
-	{
-		fprintf(stderr, "pid %ld: killed by signal %d\n",
-			(long) pid, WTERMSIG(status));
-	}
+	if ( WIFSIGNALED(status) )
+		errmsg( "%s: %s%s\n", cmd, strsignal(WTERMSIG(status)),
+			WCOREDUMP(status) ? " (core dumped)" : "" );
 }
 
 
@@ -864,53 +822,37 @@ runCmd(const char * cmd)
 static void
 childProcess(const char * cmd)
 {
-	const char **	argv;
+	char	*const	*argv;
 	int		argc;
 
 	/*
-	 * Close any extra file descriptors we have opened.
-	 */	
-	while (--sourceCount >= 0)
-	{
-		if (sourcefiles[sourceCount] != stdin)
-			fclose(sourcefiles[sourceCount]);
-	}
-
-	/*
 	 * Break the command line up into individual arguments.
 	 * If this fails, then run the shell to execute the command.
 	 */
-	if (!makeArgs(cmd, &argc, &argv))
-	{
-		system(cmd);
-		exit(0);
-	}
+	if ( !makeArgs(cmd, &argc, (const char *const **)&argv) )
+		exit( system( cmd ) );
 
 	/*
 	 * Try to execute the program directly.
 	 */
-	execvp(argv[0], (char **) argv);
+	execvp( argv[0], argv );
 
 	/*
 	 * The exec failed, so try to run the command using the shell
 	 * in case it is a shell script.
 	 */
-	if (errno == ENOEXEC)
-	{
-		system(cmd);
-		exit(0);
-	}
+	if ( errno == ENOEXEC )
+		exit( system( cmd ) );
 
 	/*
 	 * There was something else wrong, complain and exit.
 	 */
-	perror(argv[0]);
-	exit(1);
+	fatal( "exec: %s: %s", argv[0], strerror(errno) );
 }
 
 
 void
-do_help(int argc, const char ** argv)
+do_help(int argc, const char *const *argv)
 {
 	const CommandEntry *	entry;
 	const char *		str;
@@ -955,7 +897,7 @@ do_help(int argc, const char ** argv)
 
 
 void
-do_alias(int argc, const char ** argv)
+do_alias(int argc, const char *const *argv)
 {
 	const char *	name;
 	char *		value;
@@ -982,14 +924,14 @@ do_alias(int argc, const char ** argv)
 		if (alias)
 			printf("%s\n", alias->value);
 		else
-			fprintf(stderr, "Alias \"%s\" is not defined\n", name);
+			errmsg( "alias \"%s\" is not defined", name );
 
 		return;	
 	}
 
-	if (strcmp(name, "alias") == 0)
+	if ( !strcmp(name, "alias") )
 	{
-		fprintf(stderr, "Cannot alias \"alias\"\n");
+		errmsg( "cannot alias \"alias\"");
 
 		return;
 	}
@@ -997,17 +939,14 @@ do_alias(int argc, const char ** argv)
 	if (!makeString(argc - 2, argv + 2, buf, CMD_LEN))
 		return;
 
-	value = malloc(strlen(buf) + 1);
+	value = strdup( buf );
 
-	if (value == NULL)
+	if ( !value )
 	{
-		fprintf(stderr, "No memory for alias value\n");
-
+		errmsg( "failed to alias \"%s\": %s", name, strerror( errno ) );
 		return;
 	}
 
-	strcpy(value, buf);
-
 	alias = findAlias(name);
 
 	if (alias)
@@ -1018,7 +957,7 @@ do_alias(int argc, const char ** argv)
 		return;
 	}
 
-	if ((aliasCount % ALIAS_ALLOC) == 0)
+	if ( !(aliasCount % ALIAS_ALLOC) )
 	{
 		count = aliasCount + ALIAS_ALLOC;
 
@@ -1030,11 +969,10 @@ do_alias(int argc, const char ** argv)
 		else
 			alias = (Alias *) malloc(sizeof(Alias) * count);
 
-		if (alias == NULL)
+		if ( !alias )
 		{
-			free(value);
-			fprintf(stderr, "No memory for alias table\n");
-
+			free( value );
+			errmsg( "failed to alias \"%s\": %s", name, strerror( errno ) );
 			return;
 		}
 
@@ -1043,19 +981,17 @@ do_alias(int argc, const char ** argv)
 
 	alias = &aliasTable[aliasCount];
 
-	alias->name = malloc(strlen(name) + 1);
+	alias->name = strdup( name );
 
-	if (alias->name == NULL)
+	if ( !alias->name )
 	{
-		free(value);
-		fprintf(stderr, "No memory for alias name\n");
-
+		free( value );
+		errmsg( "failed to alias \"%s\": %s", name, strerror( errno ) );
 		return;
 	}
 
-	strcpy(alias->name, name);
 	alias->value = value;
-	aliasCount++;
+	++aliasCount;
 }
 
 
@@ -1064,25 +1000,22 @@ do_alias(int argc, const char ** argv)
  * using the names without the dash.
  */
 void
-do_aliasall(int argc, const char **argv)
+do_aliasall(int argc, const char *const *argv)
 {
-	const CommandEntry *	entry;
-	const char *		name;
-	const char *		newArgv[4];
+	const	CommandEntry	*entry;
 
-	for (entry = commandEntryTable; entry->name; entry++)
+	for ( entry = commandEntryTable; entry->name; ++entry )
 	{
-		name = entry->name;
+		const	char	*name = entry->name;
 
 		if (*name != '-')
 			continue;
+		else
+		{
+			const char	*const	newArgv[] = { "alias", name + 1, name, NULL };
 
-		newArgv[0] = "alias";
-		newArgv[1] = name + 1;
-		newArgv[2] = name;
-		newArgv[3] = NULL;
-
-		do_alias(3, newArgv);
+			do_alias(3, newArgv);
+		}
 	}
 }
 
@@ -1110,60 +1043,40 @@ findAlias(const char * name)
 
 
 void
-do_source(int argc, const char ** argv)
+do_source(int argc, const char *const *argv)
 {
 	readFile(argv[1]);
 }
 
 
 void
-do_exec(int argc, const char ** argv)
+do_exec(int argc, const char *const *argv)
 {
-	const char *	name;
-
-	name = argv[1];
-
-	if (access(name, 4))
-	{
-		perror(name);
-
-		return;
-	}
-
-	while (--sourceCount >= 0)
-	{
-		if (sourcefiles[sourceCount] != stdin)
-			fclose(sourcefiles[sourceCount]);
-	}
-
-	argv[argc] = NULL;
-
-	execvp(name, (char **) argv + 1);
-	exit(1);
+	char *const *av = (char *const *)argv;
+	const char	*const	name = av[1];
+	execvp( name, av + 1);
+	errmsg( "exec: %s: %s", name, strerror(errno) );
 }
 
 
 void
-do_prompt(int argc, const char ** argv)
+do_prompt(int argc, const char *const *argv)
 {
 	char *	cp;
-	char	buf[CMD_LEN];
+	char	buf[1+CMD_LEN];
 
-	if (!makeString(argc - 1, argv + 1, buf, CMD_LEN))
+	if (!makeString(argc - 1, argv + 1, buf, sizeof(buf)))
 		return;
 
-	cp = malloc(strlen(buf) + 2);
+	strcat( buf, " " );
+	cp = strdup( buf );
 
-	if (cp == NULL)
+	if ( !cp )
 	{
-		fprintf(stderr, "No memory for prompt\n");
-
+		errmsg( "failed to set prompt: %s", strerror(errno) );
 		return;
 	}
 
-	strcpy(cp, buf);
-	strcat(cp, " ");
-
 	if (prompt)
 		free(prompt);
 
@@ -1172,7 +1085,7 @@ do_prompt(int argc, const char ** argv)
 
 
 void
-do_unalias(int argc, const char ** argv)
+do_unalias(int argc, const char *const *argv)
 {
 	Alias *	alias;
 
@@ -1192,56 +1105,28 @@ do_unalias(int argc, const char ** argv)
 }
 
 
-/*
- * Display the prompt string.
- */
-static void
-showPrompt(void)
+static	void	catchInterrupt( int no )
 {
-	const char *	cp;
-
-	cp = "> ";
-
-	if (prompt)
-		cp = prompt;
-
-	write(STDOUT, cp, strlen(cp));
-}	
-
-
-static void
-catchInt(int val)
-{
-	signal(SIGINT, catchInt);
-
 	intFlag = TRUE;
 
-	if (intCrlf)
-		write(STDOUT, "\n", 1);
+	if ( intCrlf )
+		write( STDOUT_FILENO, "\r\n", 2 );
 }
 
 
-static void
-catchQuit(int val)
+static	void	print_version( FILE *fp )
 {
-	signal(SIGQUIT, catchQuit);
-
-	intFlag = TRUE;
-
-	if (intCrlf)
-		write(STDOUT, "\n", 1);
+	fprintf( fp, "Stand-alone shell (version %s)\n", version );
 }
 
-
 /*
  * Print the usage information and quit.
  */
-static void
-usage(void)
+static	void	usage( void )
 {
-	fprintf(stderr, "Stand-alone shell (version %s)\n", version);
-	fprintf(stderr, "\n");
-	fprintf(stderr, "Usage: sash [-a] [-q] [-c command] [-p prompt]\n");
+	extern const char *__progname;
+	print_version( stderr );
+	fprintf( stderr, "Usage: %s [-a] [-q] [-e] [-c command] [-p prompt] [script]\n", __progname );
 
 	exit(1);
 }
--- sash/sash.h
+++ sash/sash.h
@@ -19,7 +19,7 @@
 #include <malloc.h>
 #include <time.h>
 #include <ctype.h>
-
+#include <errno.h>
 
 #define	PATH_LEN	1024
 #define	CMD_LEN		10240
@@ -52,56 +52,57 @@ typedef	int	BOOL;
 /*
  * Built-in command functions.
  */
-extern	void	do_alias(int argc, const char ** argv);
-extern	void	do_aliasall(int argc, const char ** argv);
-extern	void	do_cd(int argc, const char ** argv);
-extern	void	do_exec(int argc, const char ** argv);
-extern	void	do_exit(int argc, const char ** argv);
-extern	void	do_prompt(int argc, const char ** argv);
-extern	void	do_source(int argc, const char ** argv);
-extern	void	do_umask(int argc, const char ** argv);
-extern	void	do_unalias(int argc, const char ** argv);
-extern	void	do_help(int argc, const char ** argv);
-extern	void	do_ln(int argc, const char ** argv);
-extern	void	do_cp(int argc, const char ** argv);
-extern	void	do_mv(int argc, const char ** argv);
-extern	void	do_rm(int argc, const char ** argv);
-extern	void	do_chmod(int argc, const char ** argv);
-extern	void	do_mkdir(int argc, const char ** argv);
-extern	void	do_rmdir(int argc, const char ** argv);
-extern	void	do_mknod(int argc, const char ** argv);
-extern	void	do_chown(int argc, const char ** argv);
-extern	void	do_chgrp(int argc, const char ** argv);
-extern	void	do_sum(int argc, const char ** argv);
-extern	void	do_sync(int argc, const char ** argv);
-extern	void	do_printenv(int argc, const char ** argv);
-extern	void	do_more(int argc, const char ** argv);
-extern	void	do_cmp(int argc, const char ** argv);
-extern	void	do_touch(int argc, const char ** argv);
-extern	void	do_ls(int argc, const char ** argv);
-extern	void	do_dd(int argc, const char ** argv);
-extern	void	do_tar(int argc, const char ** argv);
-extern	void	do_ar(int argc, const char ** argv);
-extern	void	do_mount(int argc, const char ** argv);
-extern	void	do_umount(int argc, const char ** argv);
-extern	void	do_setenv(int argc, const char ** argv);
-extern	void	do_pwd(int argc, const char ** argv);
-extern	void	do_echo(int argc, const char ** argv);
-extern	void	do_kill(int argc, const char ** argv);
-extern	void	do_grep(int argc, const char ** argv);
-extern	void	do_file(int argc, const char ** argv);
-extern	void	do_find(int argc, const char ** argv);
-extern	void	do_ed(int argc, const char ** argv);
-extern	void	do_where(int argc, const char ** argv);
+extern	void	do_alias(int argc, const char *const *argv);
+extern	void	do_aliasall(int argc, const char *const *argv);
+extern	void	do_cd(int argc, const char *const *argv);
+extern	void	do_exec(int argc, const char *const *argv);
+extern	void	do_exit(int argc, const char *const *argv);
+extern	void	do_prompt(int argc, const char *const *argv);
+extern	void	do_source(int argc, const char *const *argv);
+extern	void	do_umask(int argc, const char *const *argv);
+extern	void	do_unalias(int argc, const char *const *argv);
+extern	void	do_help(int argc, const char *const *argv);
+extern	void	do_ln(int argc, const char *const *argv);
+extern	void	do_cp(int argc, const char *const *argv);
+extern	void	do_mv(int argc, const char *const *argv);
+extern	void	do_rm(int argc, const char *const *argv);
+extern	void	do_chmod(int argc, const char *const *argv);
+extern	void	do_mkdir(int argc, const char *const *argv);
+extern	void	do_rmdir(int argc, const char *const *argv);
+extern	void	do_mknod(int argc, const char *const *argv);
+extern	void	do_chown(int argc, const char *const *argv);
+extern	void	do_chgrp(int argc, const char *const *argv);
+extern	void	do_sum(int argc, const char *const *argv);
+extern	void	do_sync(int argc, const char *const *argv);
+extern	void	do_printenv(int argc, const char *const *argv);
+extern	void	do_more(int argc, const char *const *argv);
+extern	void	do_cmp(int argc, const char *const *argv);
+extern	void	do_touch(int argc, const char *const *argv);
+extern	void	do_ls(int argc, const char *const *argv);
+extern	void	do_dd(int argc, const char *const *argv);
+extern	void	do_tar(int argc, const char *const *argv);
+extern	void	do_ar(int argc, const char *const *argv);
+extern	void	do_mount(int argc, const char *const *argv);
+extern	void	do_umount(int argc, const char *const *argv);
+extern	void	do_setenv(int argc, const char *const *argv);
+extern	void	do_pwd(int argc, const char *const *argv);
+extern	void	do_echo(int argc, const char *const *argv);
+extern	void	do_kill(int argc, const char *const *argv);
+extern	void	do_grep(int argc, const char *const *argv);
+extern	void	do_file(int argc, const char *const *argv);
+extern	void	do_find(int argc, const char *const *argv);
+extern	void	do_ed(int argc, const char *const *argv);
+extern	void	do_where(int argc, const char *const *argv);
+extern	void	do_losetup(int argc, const char *const *argv);
 
 #ifdef	HAVE_GZIP
-extern	void	do_gzip(int argc, const char ** argv);
-extern	void	do_gunzip(int argc, const char ** argv);
+extern	void	do_gzip(int argc, const char *const *argv);
+extern	void	do_gunzip(int argc, const char *const *argv);
 #endif
 
 #ifdef	HAVE_EXT2
-extern	void	do_lsattr(int argc, const char ** argv);
-extern	void	do_chattr(int argc, const char ** argv);
+extern	void	do_lsattr(int argc, const char *const *argv);
+extern	void	do_chattr(int argc, const char *const *argv);
 #endif
 
 
@@ -124,13 +125,13 @@ extern	const char *	buildName
 	(const char * dirName, const char * fileName);
 
 extern	BOOL	makeArgs
-	(const char * cmd, int * argcPtr, const char *** argvPtr);
+	(const char * cmd, int * argcPtr, const char *const ** argvPtr);
 
 extern	BOOL	copyFile
 	(const char * srcName, const char * destName, BOOL setModes);
 
 extern	BOOL	makeString
-	(int argc, const char ** argv, char * buf, int bufLen);
+	(int argc, const char *const *argv, char * buf, int bufLen);
 
 extern	int	expandWildCards
 	(const char * fileNamePattern, const char *** retFileTable);
--- sash/utils.c
+++ sash/utils.c
@@ -520,7 +520,7 @@ expandWildCards(const char * fileNamePattern, const char *** retFileTable)
 	/*
 	 * Return the file list and count.
 	 */
-	*retFileTable = (const char **) fileTable;
+	*retFileTable = (const char **)fileTable;
 
 	return fileCount;
 }
@@ -532,11 +532,11 @@ expandWildCards(const char * fileNamePattern, const char *** retFileTable)
 int
 nameSort(const void * p1, const void * p2)
 {
-	const char **	s1;
-	const char **	s2;
+	const char *const *	s1;
+	const char *const *	s2;
 
-	s1 = (const char **) p1;
-	s2 = (const char **) p2;
+	s1 = (const char *const *) p1;
+	s2 = (const char *const *) p2;
 
 	return strcmp(*s1, *s2);
 }
@@ -646,14 +646,14 @@ match(const char * text, const char * pattern)
  * already output.
  */
 BOOL
-makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
+makeArgs(const char * cmd, int * retArgc, const char *const ** retArgv)
 {
 	const char *		argument;
 	char *			cp;
 	char *			cpOut;
 	char *			newStrings;
-	const char **		fileTable;
-	const char **		newArgTable;
+	const	char	**fileTable;
+	const	char	**newArgTable;
 	int			newArgTableSize;
 	int			fileCount;
 	int			len;
@@ -900,7 +900,7 @@ makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
 		 * Copy the new arguments to the end of the old ones.
 		 */
 		memcpy((void *) &argTable[argCount], (const void *) fileTable,
-			(sizeof(const char **) * fileCount));
+			(sizeof(const char *const *) * fileCount));
 
 		/*
 		 * Add to the argument count.
@@ -914,7 +914,7 @@ makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
 	argTable[argCount] = NULL;
 
 	*retArgc = argCount;
-	*retArgv = argTable;
+	*retArgv = (const char *const *)argTable;
 
  	return TRUE;
 }
@@ -929,7 +929,7 @@ makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
 BOOL
 makeString(
 	int		argc,
-	const char **	argv,
+	const char *const *	argv,
 	char *		buf,
 	int		bufLen
 )
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin