]> err.no Git - sash/commitdiff
Imported Upstream version 3.4 upstream/3.4
authorTollef Fog Heen <tfheen@err.no>
Sun, 20 Apr 2014 06:13:08 +0000 (08:13 +0200)
committerTollef Fog Heen <tfheen@err.no>
Sun, 20 Apr 2014 06:13:08 +0000 (08:13 +0200)
17 files changed:
Makefile
README
cmd_ar.c [new file with mode: 0644]
cmd_chattr.c [new file with mode: 0644]
cmd_dd.c
cmd_ed.c
cmd_file.c [new file with mode: 0644]
cmd_find.c [new file with mode: 0644]
cmd_grep.c
cmd_gzip.c
cmd_ls.c
cmd_tar.c
cmds.c
sash.1
sash.c
sash.h
utils.c

index b7f90d4211b58a172360602e1053774389caadad..4a94167a77f9279a32bdb0263c4d1ebbf026bbf4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,11 @@
 #
 # Makefile for sash
 #
+# The HAVE_GZIP definition adds the -gzip and -gunzip commands.
+# The HAVE_EXT2 definition adds the -chattr and -lsattr comamnds.
+#
 
-CFLAGS = -O3 -Wall -DHAVE_GZIP
+CFLAGS = -O3 -Wall -Wmissing-prototypes -DHAVE_GZIP -DHAVE_EXT2
 LDFLAGS = -static -s
 LIBS = -lz
 
@@ -12,11 +15,11 @@ MANDIR = /usr/man/man1
 
 
 OBJS = sash.o cmds.o cmd_dd.o cmd_ed.o cmd_grep.o cmd_ls.o cmd_tar.o \
-       cmd_gzip.o utils.o
+       cmd_gzip.o cmd_find.o cmd_file.o cmd_chattr.o cmd_ar.o utils.o
 
 
 sash:  $(OBJS)
-       cc $(LDFLAGS) -o sash $(OBJS) $(LIBS)
+       $(CC) $(LDFLAGS) -o sash $(OBJS) $(LIBS)
 
 clean:
        rm -f $(OBJS) sash
diff --git a/README b/README
index 64a224875ba1f683bbfc4841f860c24ca90d2f8e..88137cdd6bc2123806fe4b7805feb7e9a3865a52 100644 (file)
--- a/README
+++ b/README
@@ -1,12 +1,21 @@
-This is release 2.1 of sash, my stand-alone shell for Linux.
+This is release 3.4 of sash, my stand-alone shell for Linux.
 
-The purpose of this program is to make replacing of shared libraries
-easy and safe.  It does this by firstly being linked statically, and
-secondly by including many of the standard utilities within itself.
+The purpose of this program is to make system recovery possible in
+many cases where there are missing shared libraries or executables.
+It does this by firstly being linked statically, and secondly by
+including versions of many of the standard utilities within itself.
 Read the sash.1 documentation for more details.
 
+This version has the "-ar" built-in command for extracting files
+from archive files.  This is used for unpacking Debian packages.
+Thanks to Aaron R. Crane for donating the code for this command
+(highly modified by me).
+
 Type "make install" to build and install the program and man page.
 
+Some warning messages may appear when compiling cmds.c under Linux from
+the mount.h and fs.h include files.  These warnings can be ignored.
+
 David I. Bell
 dbell@canb.auug.org.au
-March 8, 1998
+2 October 1999
diff --git a/cmd_ar.c b/cmd_ar.c
new file mode 100644 (file)
index 0000000..0653ed5
--- /dev/null
+++ b/cmd_ar.c
@@ -0,0 +1,1011 @@
+/*
+ * Original:
+ * Copyright (c) 1999 by Aaron R. Crane.
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * Modified:
+ * Copyright (c) 1999 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * The "ar" built-in command.
+ * This allows extraction and listing of ar files.
+ */
+
+#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 "sash.h"
+
+
+/*
+ * Structure to hold information about the archive file.
+ */
+typedef struct
+{
+       int             fd;             /* file reading archive from */
+       BOOL            eof;            /* end of file has been seen */
+       BOOL            rescan;         /* rescan the header just read */
+       unsigned char * nameTable;      /* long name table */
+
+       /*
+        * Information about the current file read from the archive.
+        * This is extracted from the latest member header read.
+        */
+       char *          name;           /* current file name */
+       time_t          date;           /* date of file */
+       uid_t           uid;            /* user id */
+       gid_t           gid;            /* group id */
+       mode_t          mode;           /* file protection */
+       off_t           size;           /* file size */
+       int             pad;            /* padding to next header */
+} Archive;
+
+
+/*
+ * Local procedures.
+ */
+static void    initArchive(Archive * arch);
+static BOOL    openArchive(const char * name, Archive * arch);
+static void    closeArchive(Archive * arch);
+static BOOL    readSpecialMember(Archive * arch);
+static BOOL    readNormalMember(Archive * arch);
+static BOOL    readMember(Archive * arch, struct ar_hdr * hdr);
+static BOOL    skipMember(const Archive * arch);
+static BOOL    skipPadding(int fd, int pad);
+static BOOL    writeFile(const Archive * arch, int outfd);
+static int     createFile(const Archive * arch);
+static BOOL    canonicalize(Archive * arch, const struct ar_hdr * hdr);
+static void    listMember(const Archive * arch);
+static int     shortNameMatches44BSD(const char * name);
+static int     shortNameMatchesSysV(const char * name);
+
+static BOOL    wantMember(const Archive * arch, int n_names,
+                       const char ** names);
+
+static BOOL    getNumber(const char * s, unsigned base, int max_digits,
+                       unsigned long * ul);
+
+
+void
+do_ar(int argc, const char ** argv)
+{
+       const char *    options;
+       const char *    archiveName;
+       BOOL            doExtract;
+       BOOL            doTable;
+       BOOL            doPrint;
+       BOOL            verbose;
+       Archive         arch;
+
+       verbose = FALSE;
+       doExtract = FALSE;
+       doTable = FALSE;
+       doPrint = FALSE;
+
+       if (argc < 3)
+       {
+               fprintf(stderr, "Too few arguments for ar\n");
+
+               return;
+       }
+
+       /*
+        * Get the option string and archive file name.
+        */
+       options = argv[1];
+       archiveName = argv[2];
+
+       /*
+        * Advance the arguments to the list of file names (if any).
+        */
+       argc -= 3;
+       argv += 3;
+
+       /*
+        * Parse the option characters.
+        */
+       for (; *options; options++)
+       {
+               switch (*options)
+               {
+               case 't':
+                       doTable = TRUE;
+                       break;
+
+               case 'x':
+                       doExtract = TRUE;
+                       break;
+
+               case 'p':
+                       doPrint = TRUE;
+                       break;
+
+               case 'v':
+                       verbose = TRUE;
+                       break;
+
+               case 'd': case 'm': case 'q': case 'r':
+                       fprintf(stderr, "Writing ar files is not supported\n");
+
+                       return;
+
+               default:
+                       fprintf(stderr, "Unknown ar flag: %c\n", *options);
+
+                       return;
+               }
+       }
+
+       if (doExtract + doTable + doPrint != 1)
+       {
+               fprintf(stderr,
+                       "Exactly one of 'x', 'p' or 't' must be specified\n");
+
+               return;
+       }
+
+       /*
+        * Open the archive file.
+        */
+       initArchive(&arch);
+
+       if (!openArchive(archiveName, &arch))
+               return;
+
+       /*
+        * Read the first special member of the archive.
+        */
+       if (!readSpecialMember(&arch))
+               return;
+
+       /*
+        * Read all of the normal members of the archive.
+        */
+       while (readNormalMember(&arch))
+       {
+               /*
+                * If this file is not wanted then skip it.
+                */
+               if (!wantMember(&arch, argc, argv))
+               {
+                       if (!skipMember(&arch))
+                               break;
+
+                       continue;
+               }
+
+               /*
+                * This file is wanted.
+                */
+               if (doTable)
+               {
+                       if (verbose)
+                               listMember(&arch);
+                       else
+                               puts(arch.name);
+
+                       if (!skipMember(&arch))
+                               break;
+               }
+               else if (doPrint)
+               {
+                       if (verbose)
+                       {
+                               /*
+                                * The verbose format makes me gag,
+                                * but 4.4BSD, GNU and even V7 all
+                                * have the same lossage.
+                                */
+                               printf("\n<%s>\n\n", arch.name);
+                               fflush(stdout);
+                       }
+
+                       if (!writeFile(&arch, STDOUT))
+                               break;
+               }
+               else if (doExtract)
+               {
+                       int     outfd;
+                       BOOL    success;
+
+                       if (verbose)
+                               printf("x - %s\n", arch.name);
+
+                       outfd = createFile(&arch);
+
+                       if (outfd == -1)
+                               break;
+
+                       success = writeFile(&arch, outfd);
+
+                       if (close(outfd) == -1)
+                       {
+                               fprintf(stderr, "Can't close %s: %s\n",
+                                       arch.name, strerror(errno));
+
+                               break;
+                       }
+
+                       if (!success)
+                               break;
+               }
+               else
+               {
+                       fprintf(stderr, "Oops -- I don't know what to do\n");
+                       break;
+               }
+       }
+
+       closeArchive(&arch);
+}
+
+
+/*
+ * Open the file for writing for the specified archive structure,
+ * setting its mode and owner if possible.  Returns the file handle
+ * of the opened file, or -1 on an error.
+ */
+static int
+createFile(const Archive * arch)
+{
+       int     fd;
+
+       fd = open(arch->name, O_WRONLY | O_CREAT | O_TRUNC, arch->mode);
+
+       if (fd == -1)
+       {
+               fprintf(stderr, "Can't create \"%s\": %s\n",
+                       arch->name, strerror(errno));
+
+               return -1;
+       }
+
+       /*
+        * Don't worry if these fail.  We have to do the fchmod() despite
+        * specifying the mode in the open() call, because that mode is
+        * munged by the umask.
+        */
+       (void) fchmod(fd, arch->mode);
+       (void) fchown(fd, arch->uid, arch->gid);
+
+       return fd;
+}
+
+
+/*
+ * Return whether the current archive member is wanted.
+ * This means that the file name is contained in the specified list of
+ * 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)
+{
+       int     i;
+
+       /*
+        * If there are no names then all archive members are wanted.
+        */
+       if (n_names == 0)
+               return TRUE;
+
+       /*
+        * See if the member file name is contained in the list.
+        */
+       for (i = 0; i < n_names; i++)
+       {
+               if (strcmp(arch->name, name[i]) == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+/*
+ * Parse a number from the specified string in the specified base.
+ * The number is terminated by a null, space, or the specified number
+ * of digits.  The number is returned through the supplied pointer.
+ * Only non-negatives numbers are parsed.  Returns TRUE on success.
+ */
+static BOOL
+getNumber(const char * s, unsigned base, int max_digits, unsigned long * ul)
+{
+       const char *    p;
+       const char *    endp;
+       unsigned long   cutoff;
+       unsigned long   cutlim;
+
+       if (base < 2 || base > 10  ||  s == 0 || *s == 0  ||  ul == 0)
+               return FALSE;
+
+       cutoff = ULONG_MAX / (unsigned long) base;
+       cutlim = ULONG_MAX % (unsigned long) base;
+       *ul = 0;
+
+       endp = (max_digits >= 0) ? s + max_digits : 0;
+
+       for (p = s;  endp ? p < endp : 1;  p++)
+       {
+               unsigned d;
+
+               if (*p == 0 || *p == ' ')
+                       break;  /* end of string */
+
+               if (!isDecimal(*p))
+                       return FALSE; /* non-digit */
+
+               d = *p - '0';
+
+               if (d >= base)
+                       return FALSE; /* digit outside range for base */
+
+               if (*ul > cutoff || (*ul == cutoff && d > cutlim))
+                       return FALSE; /* overflow */
+
+               *ul *= base;
+               *ul += d;
+       }
+
+       if (p == s)
+               return FALSE;   /* nothing was converted */
+
+       if (*p && *p != ' ')
+               return FALSE;   /* trailing garbage */
+
+       return TRUE;
+}
+
+
+/*
+ * Initialise the specified Archive structure for use.
+ */
+static void
+initArchive(Archive * arch)
+{
+       arch->fd = -1;
+       arch->name = 0;
+       arch->nameTable = 0;
+       arch->eof = FALSE;
+       arch->rescan = FALSE;
+}
+
+
+/*
+ * Open the specified archive file name and read the header from it.
+ * The file handle is saved in the Archive structure for further use.
+ * Returns TRUE on success.
+ */
+static BOOL
+openArchive(const char * name, Archive * arch)
+{
+       unsigned char   buf[SARMAG];
+       ssize_t         cc;
+
+       arch->fd = open(name, O_RDONLY);
+
+       if (arch->fd == -1)
+       {
+               fprintf(stderr, "Can't open archive file %s: %s\n",
+                       name, strerror(errno));
+
+               return FALSE;
+       }
+
+       cc = read(arch->fd, buf, SARMAG);
+
+       if (cc == -1)
+       {
+               fprintf(stderr, "Error reading archive header: %s\n",
+                       strerror(errno));
+
+               goto close_and_out;
+       } 
+
+       if (cc != SARMAG)
+       {
+               fprintf(stderr, "Short read of archive header\n");
+
+               goto close_and_out;
+       }
+
+       if (memcmp(buf, ARMAG, SARMAG))
+       {
+               fprintf(stderr, "Invalid archive header\n");
+
+               goto close_and_out;
+       }
+
+       return TRUE;
+
+
+       /*
+        * Here on an error to clean up.
+        */
+close_and_out:
+       (void) close(arch->fd);
+       arch->fd = -1;
+
+       return FALSE;
+}
+
+
+/*
+ * Close the archive file.
+ */
+static void
+closeArchive(Archive * arch)
+{
+       free(arch->name);
+       arch->name = 0;
+
+       free(arch->nameTable);
+       arch->nameTable = 0;
+
+       if (arch->fd >= 0)
+               (void) close(arch->fd);
+
+       arch->fd = -1;
+}
+
+
+/*
+ * Read an archive member header into the specified structure.
+ * Returns TRUE on success.  On failure, the eof flag is set if
+ * the end of file had been reached.
+ */
+static BOOL
+readMember(Archive * arch, struct ar_hdr * hdr)
+{
+       ssize_t cc;
+
+       cc = read(arch->fd, hdr, sizeof(*hdr));
+
+       if (cc < 0)
+       {
+               fprintf(stderr, "Error reading member header: %s\n",
+                       strerror(errno));
+
+               return FALSE;
+       }
+
+       if (cc == 0)
+       {
+               arch->eof = TRUE;
+
+               return FALSE;
+       }
+
+       if (cc != sizeof(*hdr))
+       {
+               fprintf(stderr, "Short read of member header\n");
+
+               return FALSE;
+       }
+
+       if (memcmp(hdr->ar_fmag, ARFMAG, sizeof(hdr->ar_fmag)))
+       {
+               fprintf(stderr, "Invalid member header\n");
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
+/*
+ * Check the member file name and see if it matches the 4.4 BSD
+ * syntax for long file names.  If so, return the number of characters
+ * of the actual long file name.  Returns -1 on an error.
+ */
+static int
+shortNameMatches44BSD(const char * name)
+{
+       const char *    p;
+       unsigned long   ul;
+
+       if (strncmp(name, "#1/", 3) != 0)
+               return -1;
+
+       if (!isDecimal(name[3]))
+               return -1;
+
+       for (p = name + 4;  *p;  p++)
+       {
+               if (!isDecimal(*p))
+                       break;
+       }
+
+       while (*p)
+       {
+               if (*p++ != ' ')
+                       return -1;
+       }
+
+       if (!getNumber(name + 3, 10, -1, &ul))
+               return -1;
+
+       if (ul == 0)            /* broken archive */
+               return -1;
+
+       return ul;
+}
+
+
+/*
+ * Check the member file name and see if it matches the SYS V syntax
+ * for long file names.  If so, return the number of characters of the
+ * actual long file name.  Returns -1 on an error.
+ */
+static int
+shortNameMatchesSysV(const char * name)
+{
+       const char *    p;
+       unsigned long   ul;
+
+       /* "^/(\d+) *$" */
+       if (name[0] != '/')
+               return -1;
+
+       if (!isDecimal(name[1]))
+               return -1;
+
+       for (p = name + 2;  *p;  p++)
+       {
+               if (!isDecimal(*p))
+                       break;
+       }
+
+       while (*p)
+       {
+               if (*p++ != ' ')
+                       return -1;
+       }
+
+       if (!getNumber(name + 1, 10, -1, &ul))
+               return -1;
+
+       return ul;
+}
+
+
+#define MEMB_NAME_ALLOC(n)                                     \
+       do                                                      \
+       {                                                       \
+               arch->name = malloc(n);                         \
+               if (!arch->name)                                \
+               {                                               \
+                       fprintf(stderr, "Out of memory\n");     \
+                       return FALSE;                           \
+               }                                               \
+       } while (0);
+
+
+/*
+ * Examine the archive structure that was read and fill in the
+ * current member data with the extracted information.  This handles
+ * various types of archive headers.  This can read data from the
+ * archive file to obtain a long file name.  Returns TRUE on success.
+ */
+static BOOL
+canonicalize(Archive * arch, const struct ar_hdr * hdr)
+{
+       char            buf[sizeof(hdr->ar_name) + 1];
+       int             n;
+       unsigned long   ul;
+       unsigned long   bsd_len;
+
+       bsd_len = 0;
+
+       free(arch->name);
+       arch->name = 0;
+
+       strncpy(buf, hdr->ar_name, sizeof(hdr->ar_name));
+       buf[sizeof(hdr->ar_name)] = 0;
+
+       /*
+        * 1. If shortname matches "^#1/(\d+) *$", then it's a 4.4BSD
+        *    longname.  Read a longname of $1 bytes from ARCH->fd, or
+        *    return FALSE if impossible.
+        */
+       if ((n = shortNameMatches44BSD(buf)) != -1)
+       {
+               /* N is the length of the longname */
+               ssize_t cc;
+
+               bsd_len = n;
+
+               MEMB_NAME_ALLOC(n + 1);
+
+               cc = read(arch->fd, arch->name, n);
+
+               if (cc == -1)
+               {
+                       fprintf(stderr, "Error reading longname: %s\n",
+                               strerror(errno));
+
+                       goto free_and_out;
+               }
+
+               if (cc != n)
+               {
+                       fprintf(stderr, "Unexpected end of file in longname\n");
+
+                       goto free_and_out;
+               }
+
+               arch->name[n] = 0;
+       }
+
+       /*
+        * 2. Otherwise, if shortname matches "^/(\d+) *$", then it's a SysV
+        *    longname.  Get the longname from the nameTable, or return FALSE
+        *    if there is none.
+        */
+       else if ((n = shortNameMatchesSysV(buf)) != -1)
+       {
+               /*
+                * N is the index of the longname
+                */
+               const char *    longname;
+               const char *    p;
+               size_t          len;
+
+               if (n >= strlen(arch->nameTable))
+               {
+                       fprintf(stderr, "Longname index too large\n");
+
+                       return FALSE;
+               }
+
+               longname = arch->nameTable + n;
+
+               p = strchr(longname, '/');
+
+               if (!p)
+               {
+                       fprintf(stderr, "Bad longname index\n");
+
+                       return FALSE;
+               }
+
+               if (p[1] != '\n')
+               {
+                       fprintf(stderr, "Malformed longname table\n");
+
+                       return FALSE;
+               }
+
+               len = p - longname;
+               MEMB_NAME_ALLOC(len + 1);
+               strncpy(arch->name, longname, len);
+               arch->name[len] = '\0';
+       }
+
+       /*
+        * 3. Otherwise, it's just a shortname.  If the shortname contains a
+        *    slash, then the name terminates before the slash; otherwise,
+        *    the name terminates at the first space, or at the end of the
+        *    field if there is none. */
+       else
+       {
+               const char *    p;
+               size_t          len;
+
+               p = strchr(buf, '/');
+
+               if (!p)
+                       p = strchr(buf, ' ');
+
+               if (p)
+                       len = p - buf;
+               else
+                       len = sizeof(hdr->ar_name);
+
+               MEMB_NAME_ALLOC(len + 1);
+               strncpy(arch->name, buf, len);
+               arch->name[len] = 0;
+       }
+
+       /*
+        * 4. Parse the remaining fields of the header.  Return FALSE if any
+        *    are missing or ill-formed.
+        */
+#define FIELD(AFIELD, MFIELD, base)                                    \
+       if (!getNumber(hdr->AFIELD, base, sizeof(hdr->AFIELD), &ul))    \
+       {                                                               \
+               fprintf(stderr, "Malformed archive member header\n");   \
+               goto free_and_out;                                      \
+       }                                                               \
+       arch->MFIELD = ul;
+
+       FIELD(ar_date, date, 10);
+       FIELD(ar_uid,  uid,  10);
+       FIELD(ar_gid,  gid,  10);
+       FIELD(ar_mode, mode,  8);
+       FIELD(ar_size, size, 10);
+#undef FIELD
+
+       /*
+        * 4a. Decide whether a pad byte will be present.u
+        *
+        * The 4.4BSD format is really broken and needs a whole pile of
+        * cruft to deal with it.  There are several cases:
+        *
+        * 1. Even namelen, even memberlen: no pad.
+        * 2. Even namelen, odd memberlen: pad.  Just like SysV.
+        * 3. Odd namelen, even memberlen: pad.  Cruft.
+        * 4. Odd namelen, odd memberlen: no pad.  Cruft.
+        *
+        * Essentially, whenever the namelen is odd, the naive determination
+        * of whether a pad is needed is reversed.
+        */
+       if (!bsd_len)
+               arch->pad = (arch->size % 2) ? 1 : 0;
+       else
+       {
+               arch->size -= bsd_len;
+               arch->pad = (arch->size % 2) ? 1 : 0;
+
+               if (bsd_len % 2)
+                       arch->pad = !arch->pad;
+       }
+
+       /*
+        * 5. Everything was successful.
+        */
+       return TRUE;
+
+
+       /*
+        * 5a. Error exit -- free memory.
+        */
+free_and_out:
+       free(arch->name);
+       arch->name = 0;
+
+       return FALSE;
+}
+
+
+/*
+ * Skip the padding character if required to position to the
+ * beginning of the next member header.  This is done if the
+ * padding value is nonzero.  Returns TRUE on success.
+ */
+static BOOL
+skipPadding(int fd, int pad)
+{
+       if (pad)
+       {
+               if (lseek(fd, 1, SEEK_CUR) == -1)
+               {
+                       fprintf(stderr, "Can't skip pad byte: %s\n",
+                               strerror(errno));
+
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+
+/*
+ * Read the first member of the archive file and check whether it
+ * is a special one, and if so, handle it.  If the first member is
+ * a normal archive member, then set up to rescan it for the next
+ * readNormalMember call.  Returns TRUE on success.
+ */
+static BOOL
+readSpecialMember(Archive * arch)
+{
+       struct ar_hdr   hdr;
+
+       /*
+        * 1. Read a header H.  Fail if impossible.
+        */
+       if (!readMember(arch, &hdr))
+               return FALSE;
+
+       /*
+        * 2. If H is a symbol table, ditch it.
+        * Fail if impossible.
+        */
+       if ((strncmp(hdr.ar_name, "/ ", 2) == 0) ||
+               (strncmp(hdr.ar_name, "__.SYMTAB       ",
+                       sizeof(hdr.ar_name)) == 0))
+       {
+               if (!canonicalize(arch, &hdr))
+                       return FALSE;
+
+               return skipMember(arch);
+       }
+
+       /*
+        * 3. If H is a SysV longname table, read it into ARCH.
+        */
+       if (strncmp(hdr.ar_name, "//", 2) == 0)
+       {
+               unsigned long   len;
+               ssize_t         cc;
+
+               if (!getNumber(hdr.ar_size, 10, sizeof(hdr.ar_size), &len))
+               {
+                       fprintf(stderr, "Invalid name-table size\n");
+
+                       return FALSE;
+               }
+
+               arch->nameTable = malloc(len + 1);
+
+               if (!arch->nameTable)
+               {
+                       fprintf(stderr, "Out of memory\n");
+
+                       return FALSE;
+               }
+
+               cc = read(arch->fd, arch->nameTable, len);
+
+               if (cc == -1)
+               {
+                       fprintf(stderr, "Error reading name-table: %s\n",
+                               strerror(errno));
+
+                       return FALSE;
+               }
+
+               if (cc != (ssize_t) len)
+               {
+                       fprintf(stderr,
+                               "Unexpected end of file in name-table\n");
+
+                       return FALSE;
+               }
+
+               arch->nameTable[len] = 0;
+
+               return skipPadding(arch->fd, len % 2);
+       }
+
+       /*
+        * 4. We read a normal header.
+        * Canonicalize it, and mark it as needing rescanning.
+        */
+       arch->rescan = TRUE;
+
+       return canonicalize(arch, &hdr);
+}
+
+
+/*
+ * Read the next normal member of the archive file if possible.
+ * If the member is being rescanned, clear the rescan flag and
+ * return the header that was already read.  Returns TRUE on
+ * success.  On a failure, the eof flag will be set if end of
+ * file has been reached.
+ */
+static BOOL
+readNormalMember(Archive * arch)
+{
+       struct ar_hdr   hdr;
+
+       /*
+        * If we are rereading an old header then just clear the
+        * rescan flag and return success.
+        */
+       if (arch->rescan)
+       {
+               arch->rescan = FALSE;
+
+               return TRUE;
+       }
+
+       /*
+        * We need to read a new member header.
+        */
+       if (!readMember(arch, &hdr))
+               return FALSE;
+
+       return canonicalize(arch, &hdr);
+}
+
+
+/*
+ * Skip the current member of the archive so that we are positioned
+ * to tbe beginning of the next member's header (or end of file).
+ * Returns TRUE on success.
+ */
+static BOOL
+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));
+
+               return FALSE;
+       }
+
+       return skipPadding(arch->fd, arch->pad);
+}
+
+
+/*
+ * Copy all of the file data from the archive to the specified
+ * open file.  Returns TRUE on success.
+ */
+static BOOL
+writeFile(const Archive * arch, int outfd)
+{
+       unsigned char   buf[BUF_SIZE];
+       off_t           n;
+
+       n = arch->size;
+
+       while (n > 0)
+       {
+               ssize_t cc;
+
+               cc = read(arch->fd, buf, MIN(n, sizeof(buf)));
+
+               if (cc == -1)
+               {
+                       fprintf(stderr, "Error reading archive member: %s\n",
+                               strerror(errno));
+
+                       return FALSE;
+               }
+
+               if (cc == 0)
+               {
+                       fprintf(stderr, "Unexpected end of file\n");
+
+                       return FALSE;
+               }
+
+               if (fullWrite(outfd, buf, cc) < 0)
+               {
+                       fprintf(stderr, "Write error: %s\n", strerror(errno));
+
+                       return FALSE;
+               }
+
+               n -= cc;
+       }
+
+       if (!skipPadding(arch->fd, arch->pad))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+/*
+ * Print one line listing the information about the specified archive member.
+ */
+static void
+listMember(const Archive * arch)
+{
+       printf("%s %6ld/%-6ld %8lu %s %s\n",
+              modeString(arch->mode) + 1,
+              (long) arch->uid,
+              (long) arch->gid,
+              (unsigned long) arch->size,
+              timeString(arch->date),
+              arch->name);
+}
+
+
+/* END CODE */
diff --git a/cmd_chattr.c b/cmd_chattr.c
new file mode 100644 (file)
index 0000000..f2d98d1
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1999 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * The "chattr" and "lsattr" built-in commands.
+ * These commands are optionally built into sash.
+ * They manipulate the important ext2 file system file attribute flags.
+ */
+
+#ifdef HAVE_EXT2
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/ext2_fs.h>
+
+#include "sash.h"
+
+
+/*
+ * The chattr command.
+ * This can turn on or off the immutable and append-only ext2 flags.
+ */
+void
+do_chattr(int argc, const char ** argv)
+{
+       const char *    fileName;
+       const char *    options;
+       int *           flagPointer;
+       int             offFlags;
+       int             onFlags;
+       int             oldFlags;
+       int             newFlags;
+       int             fd;
+
+       argc--;
+       argv++;
+
+       /*
+        * Parse the options.
+        */
+       onFlags = 0;
+       offFlags = 0;
+
+       while ((**argv == '-') || (**argv == '+'))
+       {
+               options = *argv++;
+               argc--;
+
+               /*
+                * Point at the proper flags to be modified.
+                */
+               if (*options++ == '+')
+                       flagPointer = &onFlags;
+               else
+                       flagPointer = &offFlags;
+
+               /*
+                * Parse the flag characters.
+                */
+               while (*options)
+               {
+                       switch (*options++)
+                       {
+                               case 'i':
+                                       *flagPointer |= EXT2_IMMUTABLE_FL;
+                                       break;
+
+                               case 'a':
+                                       *flagPointer |= EXT2_APPEND_FL;
+                                       break;
+
+                               default:
+                                       fprintf(stderr, "Unknown flag '%c'\n",
+                                               options[-1]);
+
+                                       return;
+                       }
+               }
+       }
+
+       /*
+        * Make sure that the attributes are reasonable.
+        */
+       if ((onFlags == 0) && (offFlags == 0))
+       {
+               fprintf(stderr, "No attributes specified\n");
+
+               return;
+       }
+
+       if ((onFlags & offFlags) != 0)
+       {
+               fprintf(stderr, "Inconsistent attributes specified\n");
+
+               return;
+       }
+
+       /*
+        * Make sure there are some files to affect.
+        */
+       if (argc <= 0)
+       {
+               fprintf(stderr, "No files specified for setting attributes\n");
+
+               return;
+       }
+
+       /*
+        * Iterate over all of the file names.
+        */
+       while (argc-- > 0)
+       {
+               fileName = *argv++;
+
+               /*
+                * Open the file name.
+                */
+               fd = open(fileName, O_RDONLY | O_NONBLOCK);
+
+               if (fd < 0)
+               {
+                       perror(fileName);
+
+                       continue;
+               }
+
+               /*
+                * Read the current ext2 flag bits.
+                */
+               if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldFlags) < 0)
+               {
+                       perror(fileName);
+
+                       (void) close(fd);
+
+                       continue;
+               }
+
+               /*
+                * Modify the flag bits as specified.
+                */
+               newFlags = oldFlags;
+               newFlags |= onFlags;
+               newFlags &= ~offFlags;
+
+               /*
+                * If the flags aren't being changed, then close this
+                * file and continue.
+                */
+               if (newFlags == oldFlags)
+               {
+                       (void) close(fd);
+
+                       continue;
+               }
+
+               /*
+                * Set the new ext2 flag bits.
+                */
+               if (ioctl(fd, EXT2_IOC_SETFLAGS, &newFlags) < 0)
+               {
+                       perror(fileName);
+
+                       (void) close(fd);
+
+                       continue;
+               }
+
+               /*
+                * Close the file.
+                */
+               (void) close(fd);
+       }
+}
+
+
+/*
+ * The lsattr command.
+ * This lists the immutable and append-only ext2 flags.
+ */
+void
+do_lsattr(int argc, const char ** argv)
+{
+       const char *    fileName;
+       int             fd;
+       int             status;
+       int             flags;
+       char            string[4];
+
+       argc--;
+       argv++;
+
+       /*
+        * Iterate over all of the file names.
+        */
+       while (argc-- > 0)
+       {
+               fileName = *argv++;
+
+               /*
+                * Open the file name.
+                */
+               fd = open(fileName, O_RDONLY | O_NONBLOCK);
+
+               if (fd < 0)
+               {
+                       perror(fileName);
+
+                       continue;
+               }
+
+               /*
+                * Read the ext2 flag bits.
+                */
+               status = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
+
+               /*
+                * Close the file and check the status.
+                */
+               (void) close(fd);
+
+               if (status < 0)
+               {
+                       perror(fileName);
+
+                       continue;
+               }
+
+               /*
+                * Build up the string according to the flags.
+                * This is 'i' for immutable, and 'a' for append only.
+                */
+               string[0] = ((flags & EXT2_IMMUTABLE_FL) ? 'i' : '-');
+               string[1] = ((flags & EXT2_APPEND_FL) ? 'a' : '-');
+               string[2] = '\0';
+
+               /*
+                * Print the flags and the file name.
+                */
+               printf("%s  %s\n", string, fileName);
+       }
+}
+
+#endif
+
+
+/* END CODE */
index dfaeae594922bdcf7baa8f0ef3dfd505abe2d940..f91026154c9aa7be471d8d8ac1b19c3d4e9fbff0 100644 (file)
--- a/cmd_dd.c
+++ b/cmd_dd.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
 #define        PAR_SKIP        6
 
 
-typedef        struct {
-       char    *name;
-       int     value;
+typedef        struct
+{
+       const char *    name;
+       int             value;
 } PARAM;
 
 
@@ -36,44 +37,44 @@ static const PARAM  params[] =
 };
 
 
-static long    getnum PROTO((const char * cp));
+static long    getNum(const char * cp);
 
 
 void
-do_dd(argc, argv)
-       int             argc;
-       const char **   argv;
+do_dd(int argc, const char ** argv)
 {
        const char *    str;
        const PARAM *   par;
-       const char *    infile;
-       const char *    outfile;
+       const char *    inFile;
+       const char *    outFile;
        char *          cp;
-       int             infd;
-       int             outfd;
-       int             incc;
-       int             outcc;
-       int             blocksize;
+       int             inFd;
+       int             outFd;
+       int             inCc;
+       int             outCc;
+       int             blockSize;
        long            count;
-       long            seekval;
-       long            skipval;
+       long            seekVal;
+       long            skipVal;
        long            intotal;
-       long            outtotal;
+       long            outTotal;
        char *          buf;
-       char            localbuf[BUFSIZE];
+       char            localBuf[BUF_SIZE];
 
-       infile = NULL;
-       outfile = NULL;
-       seekval = 0;
-       skipval = 0;
-       blocksize = 512;
+       inFile = NULL;
+       outFile = NULL;
+       seekVal = 0;
+       skipVal = 0;
+       blockSize = 512;
        count = 0x7fffffff;
 
-       while (--argc > 0) {
+       while (--argc > 0)
+       {
                str = *++argv;
                cp = strchr(str, '=');
 
-               if (cp == NULL) {
+               if (cp == NULL)
+               {
                        fprintf(stderr, "Bad dd argument\n");
 
                        return;
@@ -81,36 +82,41 @@ do_dd(argc, argv)
 
                *cp++ = '\0';
 
-               for (par = params; par->name; par++) {
+               for (par = params; par->name; par++)
+               {
                        if (strcmp(str, par->name) == 0)
                                break;
                }
 
-               switch (par->value) {
+               switch (par->value)
+               {
                        case PAR_IF:
-                               if (infile) {
+                               if (inFile)
+                               {
                                        fprintf(stderr, "Multiple input files illegal\n");
 
                                        return;
                                }
        
-                               infile = cp;
+                               inFile = cp;
                                break;
 
                        case PAR_OF:
-                               if (outfile) {
+                               if (outFile)
+                               {
                                        fprintf(stderr, "Multiple output files illegal\n");
 
                                        return;
                                }
 
-                               outfile = cp;
+                               outFile = cp;
                                break;
 
                        case PAR_BS:
-                               blocksize = getnum(cp);
+                               blockSize = getNum(cp);
 
-                               if (blocksize <= 0) {
+                               if (blockSize <= 0)
+                               {
                                        fprintf(stderr, "Bad block size value\n");
 
                                        return;
@@ -119,9 +125,10 @@ do_dd(argc, argv)
                                break;
 
                        case PAR_COUNT:
-                               count = getnum(cp);
+                               count = getNum(cp);
 
-                               if (count < 0) {
+                               if (count < 0)
+                               {
                                        fprintf(stderr, "Bad count value\n");
 
                                        return;
@@ -130,9 +137,10 @@ do_dd(argc, argv)
                                break;
 
                        case PAR_SEEK:
-                               seekval = getnum(cp);
+                               seekVal = getNum(cp);
 
-                               if (seekval < 0) {
+                               if (seekVal < 0)
+                               {
                                        fprintf(stderr, "Bad seek value\n");
 
                                        return;
@@ -141,9 +149,10 @@ do_dd(argc, argv)
                                break;
 
                        case PAR_SKIP:
-                               skipval = getnum(cp);
+                               skipVal = getNum(cp);
 
-                               if (skipval < 0) {
+                               if (skipVal < 0)
+                               {
                                        fprintf(stderr, "Bad skip value\n");
 
                                        return;
@@ -158,24 +167,28 @@ do_dd(argc, argv)
                }
        }
 
-       if (infile == NULL) {
+       if (inFile == NULL)
+       {
                fprintf(stderr, "No input file specified\n");
 
                return;
        }
 
-       if (outfile == NULL) {
+       if (outFile == NULL)
+       {
                fprintf(stderr, "No output file specified\n");
 
                return;
        }
 
-       buf = localbuf;
+       buf = localBuf;
 
-       if (blocksize > sizeof(localbuf)) {
-               buf = malloc(blocksize);
+       if (blockSize > sizeof(localBuf))
+       {
+               buf = malloc(blockSize);
 
-               if (buf == NULL) {
+               if (buf == NULL)
+               {
                        fprintf(stderr, "Cannot allocate buffer\n");
 
                        return;
@@ -183,42 +196,49 @@ do_dd(argc, argv)
        }
 
        intotal = 0;
-       outtotal = 0;
+       outTotal = 0;
 
-       infd = open(infile, 0);
+       inFd = open(inFile, 0);
 
-       if (infd < 0) {
-               perror(infile);
+       if (inFd < 0)
+       {
+               perror(inFile);
 
-               if (buf != localbuf)
+               if (buf != localBuf)
                        free(buf);
 
                return;
        }
 
-       outfd = creat(outfile, 0666);
+       outFd = creat(outFile, 0666);
 
-       if (outfd < 0) {
-               perror(outfile);
-               close(infd);
+       if (outFd < 0)
+       {
+               perror(outFile);
+               close(inFd);
 
-               if (buf != localbuf)
+               if (buf != localBuf)
                        free(buf);
 
                return;
        }
 
-       if (skipval) {
-               if (lseek(infd, skipval * blocksize, 0) < 0) {
-                       while (skipval-- > 0) {
-                               incc = read(infd, buf, blocksize);
-
-                               if (incc < 0) {
-                                       perror(infile);
+       if (skipVal)
+       {
+               if (lseek(inFd, skipVal * blockSize, 0) < 0)
+               {
+                       while (skipVal-- > 0)
+                       {
+                               inCc = read(inFd, buf, blockSize);
+
+                               if (inCc < 0)
+                               {
+                                       perror(inFile);
                                        goto cleanup;
                                }
 
-                               if (incc == 0) {
+                               if (inCc == 0)
+                               {
                                        fprintf(stderr, "End of file while skipping\n");
                                        goto cleanup;
                                }
@@ -226,53 +246,60 @@ do_dd(argc, argv)
                }
        }
 
-       if (seekval) {
-               if (lseek(outfd, seekval * blocksize, 0) < 0) {
-                       perror(outfile);
+       if (seekVal)
+       {
+               if (lseek(outFd, seekVal * blockSize, 0) < 0)
+               {
+                       perror(outFile);
+
                        goto cleanup;
                }
        }
 
-       while ((incc = read(infd, buf, blocksize)) > 0) {
-               intotal += incc;
+       while ((inCc = read(inFd, buf, blockSize)) > 0)
+       {
+               intotal += inCc;
                cp = buf;
 
-               if (intflag) {
+               if (intFlag)
+               {
                        fprintf(stderr, "Interrupted\n");
                        goto cleanup;
                }
 
-               while (incc > 0) {
-                       outcc = write(outfd, cp, incc);
+               while (inCc > 0)
+               {
+                       outCc = write(outFd, cp, inCc);
 
-                       if (outcc < 0) {
-                               perror(outfile);
+                       if (outCc < 0)
+                       {
+                               perror(outFile);
                                goto cleanup;
                        }
 
-                       outtotal += outcc;
-                       cp += outcc;
-                       incc -= outcc;
+                       outTotal += outCc;
+                       cp += outCc;
+                       inCc -= outCc;
                }
        }
 
-       if (incc < 0)
-               perror(infile);
+       if (inCc < 0)
+               perror(inFile);
 
 cleanup:
-       close(infd);
+       close(inFd);
 
-       if (close(outfd) < 0)
-               perror(outfile);
+       if (close(outFd) < 0)
+               perror(outFile);
 
-       if (buf != localbuf)
+       if (buf != localBuf)
                free(buf);
 
-       printf("%ld+%d records in\n", intotal / blocksize,
-               (intotal % blocksize) != 0);
+       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);
 }
 
 
@@ -281,20 +308,20 @@ cleanup:
  * Returns -1 if the number format is illegal.
  */
 static long
-getnum(cp)
-       const char *    cp;
+getNum(const char * cp)
 {
        long    value;
 
-       if (!isdecimal(*cp))
+       if (!isDecimal(*cp))
                return -1;
 
        value = 0;
 
-       while (isdecimal(*cp))
+       while (isDecimal(*cp))
                value = value * 10 + *cp++ - '0';
 
-       switch (*cp++) {
+       switch (*cp++)
+       {
                case 'k':
                        value *= 1024;
                        break;
index c3e522343a59bfc113a5e6cd0c767560a46e8c3b..9b4b383d27bb7c39b1ec351046291db581a94339 100644 (file)
--- a/cmd_ed.c
+++ b/cmd_ed.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
@@ -9,14 +9,16 @@
 #include "sash.h"
 
 #define        USERSIZE        1024    /* max line length typed in by user */
-#define        INITBUFSIZE     1024    /* initial buffer size */
+#define        INITBUF_SIZE    1024    /* initial buffer size */
 
 
 typedef        int     NUM;
 typedef        int     LEN;
 
 typedef        struct  LINE    LINE;
-struct LINE    {
+
+struct LINE
+{
        LINE *  next;
        LINE *  prev;
        LEN     len;
@@ -25,76 +27,73 @@ struct      LINE    {
 
 
 static LINE    lines;
-static LINE *  curline;
-static NUM     curnum;
-static NUM     lastnum;
+static LINE *  curLine;
+static NUM     curNum;
+static NUM     lastNum;
 static NUM     marks[26];
 static BOOL    dirty;
-static char *  filename;
-static char    searchstring[USERSIZE];
-
-static char *  bufbase;
-static char *  bufptr;
-static LEN     bufused;
-static LEN     bufsize;
-
-
-static void    docommands PROTO((void));
-static void    subcommand PROTO((const char * cmd, NUM num1, NUM num2));
-
-static BOOL    getnum
-       PROTO((const char ** retcp, BOOL * rethavenum, NUM * retnum));
-
-static BOOL    setcurnum PROTO((NUM num));
-static BOOL    initedit PROTO((void));
-static void    termedit PROTO((void));
-static void    addlines PROTO((NUM num));
-static BOOL    insertline PROTO((NUM num, const char * data, LEN len));
-static BOOL    deletelines PROTO((NUM num1, NUM num2));
-static BOOL    printlines PROTO((NUM num1, NUM num2, BOOL expandflag));
-static BOOL    writelines PROTO((const char * file, NUM num1, NUM num2));
-static BOOL    readlines PROTO((const char * file, NUM num));
-static NUM     searchlines PROTO((const char * str, NUM num1, NUM num2));
-
-static LEN     findstring
-       PROTO((const LINE * lp, const char * str, LEN len, LEN offset));
-
-static LINE *  findline PROTO((NUM num));
+static char *  fileName;
+static char    searchString[USERSIZE];
+
+static char *  bufBase;
+static char *  bufPtr;
+static LEN     bufUsed;
+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    setCurNum(NUM num);
+static BOOL    initEdit(void);
+static void    termEdit(void);
+static void    addLines(NUM num);
+static BOOL    insertLine(NUM num, const char * data, LEN len);
+static BOOL    deleteLines(NUM num1, NUM num2);
+static BOOL    printLines(NUM num1, NUM num2, BOOL expandFlag);
+static BOOL    writeLines(const char * file, NUM num1, NUM num2);
+static BOOL    readLines(const char * file, NUM num);
+static NUM     searchLines(const char * str, NUM num1, NUM num2);
+static LINE *  findLine(NUM num);
+
+static LEN     findString
+       (const LINE * lp, const char * str, LEN len, LEN offset);
 
 
 void
-do_ed(argc, argv)
-       int             argc;
-       const char **   argv;
+do_ed(int argc, const char ** argv)
 {
-       if (!initedit())
+       if (!initEdit())
                return;
 
-       if (argc > 1) {
-               filename = strdup(argv[1]);
+       if (argc > 1)
+       {
+               fileName = strdup(argv[1]);
 
-               if (filename == NULL) {
+               if (fileName == NULL)
+               {
                        fprintf(stderr, "No memory\n");
-                       termedit();
+                       termEdit();
 
                        return;
                }
 
-               if (!readlines(filename, 1)) {
-                       termedit();
+               if (!readLines(fileName, 1))
+               {
+                       termEdit();
 
                        return;
                }
 
-               if (lastnum)
-                       setcurnum(1);
+               if (lastNum)
+                       setCurNum(1);
 
                dirty = FALSE;
        }
 
-       docommands();
+       doCommands();
 
-       termedit();
+       termEdit();
 }
 
 
@@ -102,7 +101,7 @@ do_ed(argc, argv)
  * Read commands until we are told to stop.
  */
 static void
-docommands()
+doCommands(void)
 {
        const char *    cp;
        char *          endbuf;
@@ -114,8 +113,9 @@ docommands()
        BOOL            have2;
        char            buf[USERSIZE];
 
-       while (TRUE) {
-               intflag = FALSE;
+       while (TRUE)
+       {
+               intFlag = FALSE;
                printf(": ");
                fflush(stdout);
 
@@ -129,116 +129,126 @@ docommands()
 
                endbuf = &buf[len - 1];
 
-               if (*endbuf != '\n') {
+               if (*endbuf != '\n')
+               {
                        fprintf(stderr, "Command line too long\n");
 
-                       do {
+                       do
+                       {
                                len = fgetc(stdin);
-                       } while ((len != EOF) && (len != '\n'));
+                       }
+                       while ((len != EOF) && (len != '\n'));
 
                        continue;
                }
 
-               while ((endbuf > buf) && isblank(endbuf[-1]))
+               while ((endbuf > buf) && isBlank(endbuf[-1]))
                        endbuf--;
 
                *endbuf = '\0';
 
                cp = buf;
 
-               while (isblank(*cp))
+               while (isBlank(*cp))
                        cp++;
 
                have1 = FALSE;
                have2 = FALSE;
 
-               if ((curnum == 0) && (lastnum > 0)) {
-                       curnum = 1;
-                       curline = lines.next;
+               if ((curNum == 0) && (lastNum > 0))
+               {
+                       curNum = 1;
+                       curLine = lines.next;
                }
 
-               if (!getnum(&cp, &have1, &num1))
+               if (!getNum(&cp, &have1, &num1))
                        continue;
 
-               while (isblank(*cp))
+               while (isBlank(*cp))
                        cp++;
 
-               if (*cp == ',') {
+               if (*cp == ',')
+               {
                        cp++;
 
-                       if (!getnum(&cp, &have2, &num2))
+                       if (!getNum(&cp, &have2, &num2))
                                continue;
 
                        if (!have1)
                                num1 = 1;
 
                        if (!have2)
-                               num2 = lastnum;
+                               num2 = lastNum;
 
                        have1 = TRUE;
                        have2 = TRUE;
                }
 
                if (!have1)
-                       num1 = curnum;
+                       num1 = curNum;
 
                if (!have2)
                        num2 = num1;
 
-               switch (*cp++) {
+               switch (*cp++)
+               {
                        case 'a':
-                               addlines(num1 + 1);
+                               addLines(num1 + 1);
                                break;
 
                        case 'c':
-                               deletelines(num1, num2);
-                               addlines(num1);
+                               deleteLines(num1, num2);
+                               addLines(num1);
                                break;
 
                        case 'd':
-                               deletelines(num1, num2);
+                               deleteLines(num1, num2);
                                break;
 
                        case 'f':
-                               if (*cp && !isblank(*cp)) {
+                               if (*cp && !isBlank(*cp))
+                               {
                                        fprintf(stderr, "Bad file command\n");
                                        break;
                                }
 
-                               while (isblank(*cp))
+                               while (isBlank(*cp))
                                        cp++;
 
-                               if (*cp == '\0') {
-                                       if (filename)
-                                               printf("\"%s\"\n", filename);
+                               if (*cp == '\0')
+                               {
+                                       if (fileName)
+                                               printf("\"%s\"\n", fileName);
                                        else
-                                               printf("No filename\n");
+                                               printf("No file name\n");
 
                                        break;
                                }
 
                                newname = strdup(cp);
 
-                               if (newname == NULL) {
-                                       fprintf(stderr, "No memory for filename\n");
+                               if (newname == NULL)
+                               {
+                                       fprintf(stderr, "No memory for file name\n");
                                        break;
                                }
 
-                               if (filename)
-                                       free(filename);
+                               if (fileName)
+                                       free(fileName);
 
-                               filename = newname;
+                               fileName = newname;
                                break;
 
                        case 'i':
-                               addlines(num1);
+                               addLines(num1);
                                break;
 
                        case 'k':
-                               while (isblank(*cp))
+                               while (isBlank(*cp))
                                        cp++;
 
-                               if ((*cp < 'a') || (*cp > 'a') || cp[1]) {
+                               if ((*cp < 'a') || (*cp > 'a') || cp[1])
+                               {
                                        fprintf(stderr, "Bad mark name\n");
                                        break;
                                }
@@ -247,18 +257,19 @@ docommands()
                                break;
 
                        case 'l':
-                               printlines(num1, num2, TRUE);
+                               printLines(num1, num2, TRUE);
                                break;
 
                        case 'p':
-                               printlines(num1, num2, FALSE);
+                               printLines(num1, num2, FALSE);
                                break;
 
                        case 'q':
-                               while (isblank(*cp))
+                               while (isBlank(*cp))
                                        cp++;
 
-                               if (have1 || *cp) {
+                               if (have1 || *cp)
+                               {
                                        fprintf(stderr, "Bad quit command\n");
                                        break;
                                }
@@ -273,7 +284,7 @@ docommands()
                                fgets(buf, sizeof(buf), stdin);
                                cp = buf;
 
-                               while (isblank(*cp))
+                               while (isBlank(*cp))
                                        cp++;
 
                                if ((*cp == 'y') || (*cp == 'Y'))
@@ -282,85 +293,91 @@ docommands()
                                break;
 
                        case 'r':
-                               if (*cp && !isblank(*cp)) {
+                               if (*cp && !isBlank(*cp))
+                               {
                                        fprintf(stderr, "Bad read command\n");
                                        break;
                                }
 
-                               while (isblank(*cp))
+                               while (isBlank(*cp))
                                        cp++;
 
-                               if (*cp == '\0') {
-                                       fprintf(stderr, "No filename\n");
+                               if (*cp == '\0')
+                               {
+                                       fprintf(stderr, "No file name\n");
                                        break;
                                }
 
                                if (!have1)
-                                       num1 = lastnum;
+                                       num1 = lastNum;
 
-                               if (readlines(cp, num1 + 1))
+                               if (readLines(cp, num1 + 1))
                                        break;
 
-                               if (filename == NULL)
-                                       filename = strdup(cp);
+                               if (fileName == NULL)
+                                       fileName = strdup(cp);
 
                                break;
 
                        case 's':
-                               subcommand(cp, num1, num2);
+                               subCommand(cp, num1, num2);
                                break;
 
                        case 'w':
-                               if (*cp && !isblank(*cp)) {
+                               if (*cp && !isBlank(*cp))
+                               {
                                        fprintf(stderr, "Bad write command\n");
                                        break;
                                }
 
-                               while (isblank(*cp))
+                               while (isBlank(*cp))
                                        cp++;
 
                                if (!have1) {
                                        num1 = 1;
-                                       num2 = lastnum;
+                                       num2 = lastNum;
                                }
 
                                if (*cp == '\0')
-                                       cp = filename;
+                                       cp = fileName;
 
-                               if (cp == NULL) {
+                               if (cp == NULL)
+                               {
                                        fprintf(stderr, "No file name specified\n");
                                        break;
                                }
        
-                               writelines(cp, num1, num2);
+                               writeLines(cp, num1, num2);
                                break;
 
                        case 'z':
-                               switch (*cp) {
+                               switch (*cp)
+                               {
                                case '-':
-                                       printlines(curnum-21, curnum, FALSE);
+                                       printLines(curNum-21, curNum, FALSE);
                                        break;
                                case '.':
-                                       printlines(curnum-11, curnum+10, FALSE);
+                                       printLines(curNum-11, curNum+10, FALSE);
                                        break;
                                default:
-                                       printlines(curnum, curnum+21, FALSE);
+                                       printLines(curNum, curNum+21, FALSE);
                                        break;
                                }
                                break;
 
                        case '.':
-                               if (have1) {
+                               if (have1)
+                               {
                                        fprintf(stderr, "No arguments allowed\n");
                                        break;
                                }
 
-                               printlines(curnum, curnum, FALSE);
+                               printLines(curNum, curNum, FALSE);
                                break;
        
                        case '-':
-                               if (setcurnum(curnum - 1))
-                                       printlines(curnum, curnum, FALSE);
+                               if (setCurNum(curNum - 1))
+                                       printLines(curNum, curNum, FALSE);
 
                                break;
 
@@ -369,13 +386,14 @@ docommands()
                                break;
 
                        case '\0':
-                               if (have1) {
-                                       printlines(num2, num2, FALSE);
+                               if (have1)
+                               {
+                                       printLines(num2, num2, FALSE);
                                        break;
                                }
 
-                               if (setcurnum(curnum + 1))
-                                       printlines(curnum, curnum, FALSE);
+                               if (setCurNum(curNum + 1))
+                                       printLines(curNum, curNum, FALSE);
 
                                break;
 
@@ -392,37 +410,35 @@ docommands()
  * The current line is set to the last substitution done.
  */
 static void
-subcommand(cmd, num1, num2)
-       const char *    cmd;
-       NUM             num1;
-       NUM             num2;
+subCommand(const char * cmd, NUM num1, NUM num2)
 {
        int     delim;
        char *  cp;
-       char *  oldstr;
-       char *  newstr;
-       LEN     oldlen;
-       LEN     newlen;
-       LEN     deltalen;
+       char *  oldStr;
+       char *  newStr;
+       LEN     oldLen;
+       LEN     newLen;
+       LEN     deltaLen;
        LEN     offset;
        LINE *  lp;
        LINE *  nlp;
-       BOOL    globalflag;
-       BOOL    printflag;
-       BOOL    didsub;
-       BOOL    needprint;
+       BOOL    globalFlag;
+       BOOL    printFlag;
+       BOOL    didSub;
+       BOOL    needPrint;
        char    buf[USERSIZE];
 
-       if ((num1 < 1) || (num2 > lastnum) || (num1 > num2)) {
+       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+       {
                fprintf(stderr, "Bad line range for substitute\n");
 
                return;
        }
 
-       globalflag = FALSE;
-       printflag = FALSE;
-       didsub = FALSE;
-       needprint = FALSE;
+       globalFlag = FALSE;
+       printFlag = FALSE;
+       didSub = FALSE;
+       needPrint = FALSE;
 
        /*
         * Copy the command so we can modify it.
@@ -430,18 +446,20 @@ subcommand(cmd, num1, num2)
        strcpy(buf, cmd);
        cp = buf;
 
-       if (isblank(*cp) || (*cp == '\0')) {
+       if (isBlank(*cp) || (*cp == '\0'))
+       {
                fprintf(stderr, "Bad delimiter for substitute\n");
 
                return;
        }
 
        delim = *cp++;
-       oldstr = cp;
+       oldStr = cp;
 
        cp = strchr(cp, delim);
 
-       if (cp == NULL) {
+       if (cp == NULL)
+       {
                fprintf(stderr, "Missing 2nd delimiter for substitute\n");
 
                return;
@@ -449,7 +467,7 @@ subcommand(cmd, num1, num2)
 
        *cp++ = '\0';
 
-       newstr = cp;
+       newStr = cp;
        cp = strchr(cp, delim);
 
        if (cp)
@@ -457,13 +475,14 @@ subcommand(cmd, num1, num2)
        else
                cp = "";
 
-       while (*cp) switch (*cp++) {
+       while (*cp) switch (*cp++)
+       {
                case 'g':
-                       globalflag = TRUE;
+                       globalFlag = TRUE;
                        break;
 
                case 'p':
-                       printflag = TRUE;
+                       printFlag = TRUE;
                        break;
 
                default:
@@ -472,37 +491,42 @@ subcommand(cmd, num1, num2)
                        return;
        }
 
-       if (*oldstr == '\0') {
-               if (searchstring[0] == '\0') {
+       if (*oldStr == '\0')
+       {
+               if (searchString[0] == '\0')
+               {
                        fprintf(stderr, "No previous search string\n");
 
                        return;
                }
 
-               oldstr = searchstring;
+               oldStr = searchString;
        }
 
-       if (oldstr != searchstring)
-               strcpy(searchstring, oldstr);
+       if (oldStr != searchString)
+               strcpy(searchString, oldStr);
 
-       lp = findline(num1);
+       lp = findLine(num1);
 
        if (lp == NULL)
                return;
 
-       oldlen = strlen(oldstr);
-       newlen = strlen(newstr);
-       deltalen = newlen - oldlen;
+       oldLen = strlen(oldStr);
+       newLen = strlen(newStr);
+       deltaLen = newLen - oldLen;
        offset = 0;
        nlp = NULL;
 
-       while (num1 <= num2) {
-               offset = findstring(lp, oldstr, oldlen, offset);
+       while (num1 <= num2)
+       {
+               offset = findString(lp, oldStr, oldLen, offset);
 
-               if (offset < 0) {
-                       if (needprint) {
-                               printlines(num1, num1, FALSE);
-                               needprint = FALSE;
+               if (offset < 0)
+               {
+                       if (needPrint)
+                       {
+                               printLines(num1, num1, FALSE);
+                               needPrint = FALSE;
                        }
 
                        offset = 0;
@@ -512,33 +536,36 @@ subcommand(cmd, num1, num2)
                        continue;
                }
 
-               needprint = printflag;
-               didsub = TRUE;
+               needPrint = printFlag;
+               didSub = TRUE;
                dirty = TRUE;
 
                /*
                 * If the replacement string is the same size or shorter
                 * than the old string, then the substitution is easy.
                 */
-               if (deltalen <= 0) {
-                       memcpy(&lp->data[offset], newstr, newlen);
+               if (deltaLen <= 0)
+               {
+                       memcpy(&lp->data[offset], newStr, newLen);
 
-                       if (deltalen) {
-                               memcpy(&lp->data[offset + newlen],
-                                       &lp->data[offset + oldlen],
-                                       lp->len - offset - oldlen);
+                       if (deltaLen)
+                       {
+                               memcpy(&lp->data[offset + newLen],
+                                       &lp->data[offset + oldLen],
+                                       lp->len - offset - oldLen);
 
-                               lp->len += deltalen;
+                               lp->len += deltaLen;
                        }
 
-                       offset += newlen;
+                       offset += newLen;
 
-                       if (globalflag)
+                       if (globalFlag)
                                continue;
 
-                       if (needprint) {
-                               printlines(num1, num1, FALSE);
-                               needprint = FALSE;
+                       if (needPrint)
+                       {
+                               printLines(num1, num1, FALSE);
+                               needPrint = FALSE;
                        }
 
                        lp = lp->next;
@@ -552,51 +579,53 @@ subcommand(cmd, num1, num2)
                 * structure and use that.  Link it in in place of
                 * the old line structure.
                 */
-               nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltalen);
+               nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
 
-               if (nlp == NULL) {
+               if (nlp == NULL)
+               {
                        fprintf(stderr, "Cannot get memory for line\n");
 
                        return;
                }
 
-               nlp->len = lp->len + deltalen;
+               nlp->len = lp->len + deltaLen;
 
                memcpy(nlp->data, lp->data, offset);
 
-               memcpy(&nlp->data[offset], newstr, newlen);
+               memcpy(&nlp->data[offset], newStr, newLen);
 
-               memcpy(&nlp->data[offset + newlen],
-                       &lp->data[offset + oldlen],
-                       lp->len - offset - oldlen);
+               memcpy(&nlp->data[offset + newLen],
+                       &lp->data[offset + oldLen],
+                       lp->len - offset - oldLen);
 
                nlp->next = lp->next;
                nlp->prev = lp->prev;
                nlp->prev->next = nlp;
                nlp->next->prev = nlp;
 
-               if (curline == lp)
-                       curline = nlp;
+               if (curLine == lp)
+                       curLine = nlp;
 
                free(lp);
                lp = nlp;
 
-               offset += newlen;
+               offset += newLen;
 
-               if (globalflag)
+               if (globalFlag)
                        continue;
 
-               if (needprint) {
-                       printlines(num1, num1, FALSE);
-                       needprint = FALSE;
+               if (needPrint)
+               {
+                       printLines(num1, num1, FALSE);
+                       needPrint = FALSE;
                }
 
                lp = lp->next;
                num1++;
        }
 
-       if (!didsub)
-               fprintf(stderr, "No substitutions found for \"%s\"\n", oldstr);
+       if (!didSub)
+               fprintf(stderr, "No substitutions found for \"%s\"\n", oldStr);
 }
 
 
@@ -605,11 +634,7 @@ subcommand(cmd, num1, num2)
  * offset in the line.  Returns the offset of the found string, or -1.
  */
 static LEN
-findstring(lp, str, len, offset)
-       const LINE *    lp;
-       const char *    str;
-       LEN             len;
-       LEN             offset;
+findString( const LINE * lp, const char * str, LEN len, LEN offset)
 {
        LEN             left;
        const char *    cp;
@@ -618,7 +643,8 @@ findstring(lp, str, len, offset)
        cp = &lp->data[offset];
        left = lp->len - offset;
 
-       while (left >= len) {
+       while (left >= len)
+       {
                ncp = memchr(cp, *str, left);
 
                if (ncp == NULL)
@@ -649,13 +675,13 @@ findstring(lp, str, len, offset)
  * or by an end of file.
  */
 static void
-addlines(num)
-       NUM     num;
+addLines(NUM num)
 {
        int     len;
        char    buf[USERSIZE + 1];
 
-       while (fgets(buf, sizeof(buf), stdin)) {
+       while (fgets(buf, sizeof(buf), stdin))
+       {
                if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
                        return;
 
@@ -664,17 +690,20 @@ addlines(num)
                if (len == 0)
                        return;
 
-               if (buf[len - 1] != '\n') {
+               if (buf[len - 1] != '\n')
+               {
                        fprintf(stderr, "Line too long\n");
 
-                       do {
+                       do
+                       {
                                len = fgetc(stdin);
-                       } while ((len != EOF) && (len != '\n'));
+                       }
+                       while ((len != EOF) && (len != '\n'));
 
                        return;
                }
 
-               if (!insertline(num++, buf, len))
+               if (!insertLine(num++, buf, len))
                        return;
        }
 }
@@ -689,100 +718,105 @@ addlines(num)
  * The character pointer which stopped the scan is also returned.
  */
 static BOOL
-getnum(retcp, rethavenum, retnum)
-       const char **   retcp;
-       BOOL *          rethavenum;
-       NUM *           retnum;
+getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
 {
        const char *    cp;
-       char *          endstr;
+       char *          endStr;
        char            str[USERSIZE];
-       BOOL            havenum;
+       BOOL            haveNum;
        NUM             value;
        NUM             num;
        NUM             sign;
 
        cp = *retcp;
-       havenum = FALSE;
+       haveNum = FALSE;
        value = 0;
        sign = 1;
 
-       while (TRUE) {
-               while (isblank(*cp))
+       while (TRUE)
+       {
+               while (isBlank(*cp))
                        cp++;
 
-               switch (*cp) {
+               switch (*cp)
+               {
                        case '.':
-                               havenum = TRUE;
-                               num = curnum;
+                               haveNum = TRUE;
+                               num = curNum;
                                cp++;
                                break;
 
                        case '$':
-                               havenum = TRUE;
-                               num = lastnum;
+                               haveNum = TRUE;
+                               num = lastNum;
                                cp++;
                                break;
 
                        case '\'':
                                cp++;
 
-                               if ((*cp < 'a') || (*cp > 'z')) {
+                               if ((*cp < 'a') || (*cp > 'z'))
+                               {
                                        fprintf(stderr, "Bad mark name\n");
 
                                        return FALSE;
                                }
 
-                               havenum = TRUE;
+                               haveNum = TRUE;
                                num = marks[*cp++ - 'a'];
                                break;
 
                        case '/':
                                strcpy(str, ++cp);
-                               endstr = strchr(str, '/');
+                               endStr = strchr(str, '/');
 
-                               if (endstr) {
-                                       *endstr++ = '\0';
-                                       cp += (endstr - str);
-                               } else
+                               if (endStr)
+                               {
+                                       *endStr++ = '\0';
+                                       cp += (endStr - str);
+                               }
+                               else
                                        cp = "";
 
-                               num = searchlines(str, curnum, lastnum);
+                               num = searchLines(str, curNum, lastNum);
 
                                if (num == 0)
                                        return FALSE;
 
-                               havenum = TRUE;
+                               haveNum = TRUE;
                                break;
 
                        default:
-                               if (!isdecimal(*cp)) {
+                               if (!isDecimal(*cp))
+                               {
                                        *retcp = cp;
-                                       *rethavenum = havenum;
-                                       *retnum = value;
+                                       *retHaveNum = haveNum;
+                                       *retNum = value;
 
                                        return TRUE;
                                }
 
                                num = 0;
 
-                               while (isdecimal(*cp))
+                               while (isDecimal(*cp))
                                        num = num * 10 + *cp++ - '0';
 
-                               havenum = TRUE;
+                               haveNum = TRUE;
                                break;
                }
 
                value += num * sign;
 
-               while (isblank(*cp))
+               while (isBlank(*cp))
                        cp++;
 
-               switch (*cp) {
+               switch (*cp)
+               {
                        case '-':
                                sign = -1;
                                cp++;
                                break;
+
                        case '+':
                                sign = 1;
                                cp++;
@@ -790,8 +824,8 @@ getnum(retcp, rethavenum, retnum)
 
                        default:
                                *retcp = cp;
-                               *rethavenum = havenum;
-                               *retnum = value;
+                               *retHaveNum = haveNum;
+                               *retNum = value;
 
                                return TRUE;
                }
@@ -803,31 +837,32 @@ getnum(retcp, rethavenum, retnum)
  * Initialize everything for editing.
  */
 static BOOL
-initedit()
+initEdit(void)
 {
        int     i;
 
-       bufsize = INITBUFSIZE;
-       bufbase = malloc(bufsize);
+       bufSize = INITBUF_SIZE;
+       bufBase = malloc(bufSize);
 
-       if (bufbase == NULL) {
+       if (bufBase == NULL)
+       {
                fprintf(stderr, "No memory for buffer\n");
 
                return FALSE;
        }
 
-       bufptr = bufbase;
-       bufused = 0;
+       bufPtr = bufBase;
+       bufUsed = 0;
 
        lines.next = &lines;
        lines.prev = &lines;
 
-       curline = NULL;
-       curnum = 0;
-       lastnum = 0;
+       curLine = NULL;
+       curNum = 0;
+       lastNum = 0;
        dirty = FALSE;
-       filename = NULL;
-       searchstring[0] = '\0';
+       fileName = NULL;
+       searchString[0] = '\0';
 
        for (i = 0; i < 26; i++)
                marks[i] = 0;
@@ -840,29 +875,29 @@ initedit()
  * Finish editing.
  */
 static void
-termedit()
+termEdit(void)
 {
-       if (bufbase)
-               free(bufbase);
+       if (bufBase)
+               free(bufBase);
 
-       bufbase = NULL;
-       bufptr = NULL;
-       bufsize = 0;
-       bufused = 0;
+       bufBase = NULL;
+       bufPtr = NULL;
+       bufSize = 0;
+       bufUsed = 0;
 
-       if (filename)
-               free(filename);
+       if (fileName)
+               free(fileName);
 
-       filename = NULL;
+       fileName = NULL;
 
-       searchstring[0] = '\0';
+       searchString[0] = '\0';
 
-       if (lastnum)
-               deletelines(1, lastnum);
+       if (lastNum)
+               deleteLines(1, lastNum);
 
-       lastnum = 0;
-       curnum = 0;
-       curline = NULL;
+       lastNum = 0;
+       curNum = 0;
+       curLine = NULL;
 }
 
 
@@ -871,18 +906,17 @@ termedit()
  * Returns TRUE if the file was successfully read.
  */
 static BOOL
-readlines(file, num)
-       const char *    file;
-       NUM             num;
+readLines(const char * file, NUM num)
 {
        int     fd;
        int     cc;
        LEN     len;
-       LEN     linecount;
-       LEN     charcount;
+       LEN     lineCount;
+       LEN     charCount;
        char *  cp;
 
-       if ((num < 1) || (num > lastnum + 1)) {
+       if ((num < 1) || (num > lastNum + 1))
+       {
                fprintf(stderr, "Bad line for read\n");
 
                return FALSE;
@@ -890,97 +924,109 @@ readlines(file, num)
 
        fd = open(file, 0);
 
-       if (fd < 0) {
+       if (fd < 0)
+       {
                perror(file);
 
                return FALSE;
        }
 
-       bufptr = bufbase;
-       bufused = 0;
-       linecount = 0;
-       charcount = 0;
+       bufPtr = bufBase;
+       bufUsed = 0;
+       lineCount = 0;
+       charCount = 0;
        cc = 0;
 
        printf("\"%s\", ", file);
        fflush(stdout);
 
-       do {
-               if (intflag) {
+       do
+       {
+               if (intFlag)
+               {
                        printf("INTERRUPTED, ");
-                       bufused = 0;
+                       bufUsed = 0;
                        break;
                }
 
-               cp = memchr(bufptr, '\n', bufused);
+               cp = memchr(bufPtr, '\n', bufUsed);
 
-               if (cp) {
-                       len = (cp - bufptr) + 1;
+               if (cp)
+               {
+                       len = (cp - bufPtr) + 1;
 
-                       if (!insertline(num, bufptr, len)) {
+                       if (!insertLine(num, bufPtr, len))
+                       {
                                close(fd);
 
                                return FALSE;
                        }
 
-                       bufptr += len;
-                       bufused -= len;
-                       charcount += len;
-                       linecount++;
+                       bufPtr += len;
+                       bufUsed -= len;
+                       charCount += len;
+                       lineCount++;
                        num++;
 
                        continue;
                }
 
-               if (bufptr != bufbase) {
-                       memcpy(bufbase, bufptr, bufused);
-                       bufptr = bufbase + bufused;
+               if (bufPtr != bufBase)
+               {
+                       memcpy(bufBase, bufPtr, bufUsed);
+                       bufPtr = bufBase + bufUsed;
                }
 
-               if (bufused >= bufsize) {
-                       len = (bufsize * 3) / 2;
-                       cp = realloc(bufbase, len);
+               if (bufUsed >= bufSize)
+               {
+                       len = (bufSize * 3) / 2;
+                       cp = realloc(bufBase, len);
 
-                       if (cp == NULL) {
+                       if (cp == NULL)
+                       {
                                fprintf(stderr, "No memory for buffer\n");
                                close(fd);
 
                                return FALSE;
                        }
 
-                       bufbase = cp;
-                       bufptr = bufbase + bufused;
-                       bufsize = len;
+                       bufBase = cp;
+                       bufPtr = bufBase + bufUsed;
+                       bufSize = len;
                }
 
-               cc = read(fd, bufptr, bufsize - bufused);
-               bufused += cc;
-               bufptr = bufbase;
+               cc = read(fd, bufPtr, bufSize - bufUsed);
+               bufUsed += cc;
+               bufPtr = bufBase;
 
-       } while (cc > 0);
+       }
+       while (cc > 0);
 
-       if (cc < 0) {
+       if (cc < 0)
+       {
                perror(file);
                close(fd);
 
                return FALSE;
        }
 
-       if (bufused) {
-               if (!insertline(num, bufptr, bufused)) {
+       if (bufUsed)
+       {
+               if (!insertLine(num, bufPtr, bufUsed))
+               {
                        close(fd);
 
                        return -1;
                }
 
-               linecount++;
-               charcount += bufused;
+               lineCount++;
+               charCount += bufUsed;
        }
 
        close(fd);
 
-       printf("%d lines%s, %d chars\n", linecount,
-               (bufused ? " (incomplete)" : ""), charcount);
+       printf("%d lines%s, %d chars\n", lineCount,
+               (bufUsed ? " (incomplete)" : ""), charCount);
 
        return TRUE;
 }
@@ -991,24 +1037,22 @@ readlines(file, num)
  * Returns TRUE if successful, or FALSE on an error with a message output.
  */
 static BOOL
-writelines(file, num1, num2)
-       const char *    file;
-       NUM             num1;
-       NUM             num2;
+writeLines(const char * file, NUM num1, NUM num2)
 {
        int     fd;
        LINE *  lp;
-       LEN     linecount;
-       LEN     charcount;
+       LEN     lineCount;
+       LEN     charCount;
 
-       if ((num1 < 1) || (num2 > lastnum) || (num1 > num2)) {
+       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+       {
                fprintf(stderr, "Bad line range for write\n");
 
                return FALSE;
        }
 
-       linecount = 0;
-       charcount = 0;
+       lineCount = 0;
+       charCount = 0;
 
        fd = creat(file, 0666);
 
@@ -1021,34 +1065,38 @@ writelines(file, num1, num2)
        printf("\"%s\", ", file);
        fflush(stdout);
 
-       lp = findline(num1);
+       lp = findLine(num1);
 
-       if (lp == NULL) {
+       if (lp == NULL)
+       {
                close(fd);
 
                return FALSE;
        }
 
-       while (num1++ <= num2) {
-               if (write(fd, lp->data, lp->len) != lp->len) {
+       while (num1++ <= num2)
+       {
+               if (write(fd, lp->data, lp->len) != lp->len)
+               {
                        perror(file);
                        close(fd);
 
                        return FALSE;
                }
 
-               charcount += lp->len;
-               linecount++;
+               charCount += lp->len;
+               lineCount++;
                lp = lp->next;
        }
 
-       if (close(fd) < 0) {
+       if (close(fd) < 0)
+       {
                perror(file);
 
                return FALSE;
        }
 
-       printf("%d lines, %d chars\n", linecount, charcount);
+       printf("%d lines, %d chars\n", lineCount, charCount);
 
        return TRUE;
 }
@@ -1057,35 +1105,35 @@ writelines(file, num1, num2)
 /*
  * Print lines in a specified range.
  * The last line printed becomes the current line.
- * If expandflag is TRUE, then the line is printed specially to
+ * If expandFlag is TRUE, then the line is printed specially to
  * show magic characters.
  */
 static BOOL
-printlines(num1, num2, expandflag)
-       NUM     num1;
-       NUM     num2;
-       BOOL    expandflag;
+printLines(NUM num1, NUM num2, BOOL expandFlag)
 {
        const LINE *            lp;
        const unsigned char *   cp;
        int                     ch;
        LEN                     count;
 
-       if ((num1 < 1) || (num2 > lastnum) || (num1 > num2)) {
+       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+       {
                fprintf(stderr, "Bad line range for print\n");
 
                return FALSE;
        }
 
-       lp = findline(num1);
+       lp = findLine(num1);
 
        if (lp == NULL)
                return FALSE;
 
-       while (!intflag && (num1 <= num2)) {
-               if (!expandflag) {
+       while (!intFlag && (num1 <= num2))
+       {
+               if (!expandFlag)
+               {
                        write(STDOUT, lp->data, lp->len);
-                       setcurnum(num1++);
+                       setCurNum(num1++);
                        lp = lp->next;
 
                        continue;
@@ -1101,20 +1149,24 @@ printlines(num1, num2, expandflag)
                if ((count > 0) && (cp[count - 1] == '\n'))
                        count--;
 
-               while (count-- > 0) {
+               while (count-- > 0)
+               {
                        ch = *cp++;
 
-                       if (ch & 0x80) {
+                       if (ch & 0x80)
+                       {
                                fputs("M-", stdout);
                                ch &= 0x7f;
                        }
 
-                       if (ch < ' ') {
+                       if (ch < ' ')
+                       {
                                fputc('^', stdout);
                                ch += '@';
                        }
 
-                       if (ch == 0x7f) {
+                       if (ch == 0x7f)
+                       {
                                fputc('^', stdout);
                                ch = '?';
                        }
@@ -1124,7 +1176,7 @@ printlines(num1, num2, expandflag)
 
                fputs("$\n", stdout);
 
-               setcurnum(num1++);
+               setCurNum(num1++);
                lp = lp->next;
        }
 
@@ -1140,52 +1192,53 @@ printlines(num1, num2, expandflag)
  * Returns TRUE if successful.
  */
 static BOOL
-insertline(num, data, len)
-       NUM             num;
-       const char *    data;
-       LEN             len;
+insertLine(NUM num, const char * data, LEN len)
 {
-       LINE *  newlp;
+       LINE *  newLp;
        LINE *  lp;
 
-       if ((num < 1) || (num > lastnum + 1)) {
+       if ((num < 1) || (num > lastNum + 1))
+       {
                fprintf(stderr, "Inserting at bad line number\n");
 
                return FALSE;
        }
 
-       newlp = (LINE *) malloc(sizeof(LINE) + len - 1);
+       newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
 
-       if (newlp == NULL)  {
+       if (newLp == NULL) 
+       {
                fprintf(stderr, "Failed to allocate memory for line\n");
 
                return FALSE;
        }
 
-       memcpy(newlp->data, data, len);
-       newlp->len = len;
+       memcpy(newLp->data, data, len);
+       newLp->len = len;
 
-       if (num > lastnum)
+       if (num > lastNum)
                lp = &lines;
-       else {
-               lp = findline(num);
+       else
+       {
+               lp = findLine(num);
 
-               if (lp == NULL) {
-                       free((char *) newlp);
+               if (lp == NULL)
+               {
+                       free((char *) newLp);
 
                        return FALSE;
                }
        }
 
-       newlp->next = lp;
-       newlp->prev = lp->prev;
-       lp->prev->next = newlp;
-       lp->prev = newlp;
+       newLp->next = lp;
+       newLp->prev = lp->prev;
+       lp->prev->next = newLp;
+       lp->prev = newLp;
 
-       lastnum++;
+       lastNum++;
        dirty = TRUE;
 
-       return setcurnum(num);
+       return setCurNum(num);
 }
 
 
@@ -1193,43 +1246,44 @@ insertline(num, data, len)
  * Delete lines from the given range.
  */
 static BOOL
-deletelines(num1, num2)
-       NUM     num1;
-       NUM     num2;
+deleteLines(NUM num1, NUM num2)
 {
        LINE *  lp;
        LINE *  nlp;
        LINE *  plp;
        NUM     count;
 
-       if ((num1 < 1) || (num2 > lastnum) || (num1 > num2)) {
+       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+       {
                fprintf(stderr, "Bad line numbers for delete\n");
 
                return FALSE;
        }
 
-       lp = findline(num1);
+       lp = findLine(num1);
 
        if (lp == NULL)
                return FALSE;
 
-       if ((curnum >= num1) && (curnum <= num2)) {
-               if (num2 < lastnum)
-                       setcurnum(num2 + 1);
+       if ((curNum >= num1) && (curNum <= num2))
+       {
+               if (num2 < lastNum)
+                       setCurNum(num2 + 1);
                else if (num1 > 1)
-                       setcurnum(num1 - 1);
+                       setCurNum(num1 - 1);
                else
-                       curnum = 0;
+                       curNum = 0;
        }
 
        count = num2 - num1 + 1;
 
-       if (curnum > num2)
-               curnum -= count;
+       if (curNum > num2)
+               curNum -= count;
 
-       lastnum -= count;
+       lastNum -= count;
 
-       while (count-- > 0) {
+       while (count-- > 0)
+       {
                nlp = lp->next;
                plp = lp->prev;
                plp->next = nlp;
@@ -1255,42 +1309,43 @@ deletelines(num1, num2)
  * with an error printed.
  */
 static NUM
-searchlines(str, num1, num2)
-       const char *    str;
-       NUM             num1;
-       NUM             num2;
+searchLines(const char * str, NUM num1, NUM num2)
 {
        const LINE *    lp;
        int             len;
 
-       if ((num1 < 1) || (num2 > lastnum) || (num1 > num2)) {
+       if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+       {
                fprintf(stderr, "Bad line numbers for search\n");
 
                return 0;
        }
 
-       if (*str == '\0') {
-               if (searchstring[0] == '\0') {
+       if (*str == '\0')
+       {
+               if (searchString[0] == '\0')
+               {
                        fprintf(stderr, "No previous search string\n");
 
                        return 0;
                }
 
-               str = searchstring;
+               str = searchString;
        }
 
-       if (str != searchstring)
-               strcpy(searchstring, str);
+       if (str != searchString)
+               strcpy(searchString, str);
 
        len = strlen(str);
 
-       lp = findline(num1);
+       lp = findLine(num1);
 
        if (lp == NULL)
                return 0;
 
-       while (num1 <= num2) {
-               if (findstring(lp, str, len, 0) >= 0)
+       while (num1 <= num2)
+       {
+               if (findString(lp, str, len, 0) >= 0)
                        return num1;
 
                num1++;
@@ -1307,44 +1362,49 @@ searchlines(str, num1, num2)
  * Return a pointer to the specified line number.
  */
 static LINE *
-findline(num)
-       NUM     num;
+findLine(NUM num)
 {
        LINE *  lp;
        NUM     lnum;
 
-       if ((num < 1) || (num > lastnum)) {
+       if ((num < 1) || (num > lastNum))
+       {
                fprintf(stderr, "Line number %d does not exist\n", num);
 
                return NULL;
        }
 
-       if (curnum <= 0) {
-               curnum = 1;
-               curline = lines.next;
+       if (curNum <= 0)
+       {
+               curNum = 1;
+               curLine = lines.next;
        }
 
-       if (num == curnum)
-               return curline;
+       if (num == curNum)
+               return curLine;
 
-       lp = curline;
-       lnum = curnum;
+       lp = curLine;
+       lnum = curNum;
 
-       if (num < (curnum / 2)) {
+       if (num < (curNum / 2))
+       {
                lp = lines.next;
                lnum = 1;
        }
-       else if (num > ((curnum + lastnum) / 2)) {
+       else if (num > ((curNum + lastNum) / 2))
+       {
                lp = lines.prev;
-               lnum = lastnum;
+               lnum = lastNum;
        }
 
-       while (lnum < num) {
+       while (lnum < num)
+       {
                lp = lp->next;
                lnum++;
        }
 
-       while (lnum > num) {
+       while (lnum > num)
+       {
                lp = lp->prev;
                lnum--;
        }
@@ -1358,18 +1418,17 @@ findline(num)
  * Returns TRUE if successful.
  */
 static BOOL
-setcurnum(num)
-       NUM     num;
+setCurNum(NUM num)
 {
        LINE *  lp;
 
-       lp = findline(num);
+       lp = findLine(num);
 
        if (lp == NULL)
                return FALSE;
 
-       curnum = num;
-       curline = lp;
+       curNum = num;
+       curLine = lp;
 
        return TRUE;
 }
diff --git a/cmd_file.c b/cmd_file.c
new file mode 100644 (file)
index 0000000..486254d
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 1999 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * The "file" built-in command.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sash.h"
+
+
+static const char *    checkFile(const char * name);
+
+
+void
+do_file(int argc, const char ** argv)
+{
+       const char *    name;
+       const char *    info;
+
+       argc--;
+       argv++;
+
+       while (argc-- > 0)
+       {
+               name = *argv++;
+
+               info = checkFile(name);
+
+               if (info == NULL)
+                       info = "No information available";
+
+               printf("%s: %s\n", name, info);
+       }
+}
+
+
+/*
+ * Examine the specified file and return a static string which
+ * describes the file type.  Returns NULL on a failure.
+ */
+static const char *
+checkFile(const char * name)
+{
+       int             mode;
+       int             fd;
+       int             cc;
+       int             i;
+       int             ch;
+       int             badCount;
+       char *          cp;
+       struct  stat    statBuf;
+       char            data[8192];
+       static  char    info[1024];
+
+       cp = info;
+       *cp = '\0';
+
+       if (lstat(name, &statBuf) < 0)
+       {
+               if (errno == ENOENT)
+                       return "non-existent";
+
+               sprintf(cp, "stat failed: %s", strerror(errno));
+
+               return info;
+       }
+
+       /*
+        * Check the file type.
+        */
+       mode = statBuf.st_mode;
+
+       if (S_ISDIR(mode))
+               return "directory";
+
+       if (S_ISCHR(mode))
+               return "character device";
+
+       if (S_ISBLK(mode))
+               return "block device";
+
+       if (S_ISFIFO(mode))
+               return "named pipe";
+
+#ifdef S_ISLNK
+       if (S_ISLNK(mode))
+               return "symbolic link";
+#endif
+
+#ifdef S_ISSOCK
+       if (S_ISSOCK(mode))
+               return "socket";
+#endif
+
+       /*
+        * If the file is not a regular file mention that.
+        */
+       if (!S_ISREG(mode))
+       {
+               sprintf(cp, "unknown mode 0x%x, \n", mode);
+
+               cp += strlen(cp);
+       }
+
+       /*
+        * Check for an executable file.
+        */
+       if ((mode & (S_IEXEC | S_IXGRP | S_IXOTH)) != 0)
+       {
+               strcpy(cp, "executable, ");
+
+               cp += strlen(cp);
+       }
+
+       /*
+        * The file is a normal file.
+        * Open it if we can and read in the first block.
+        */
+       fd = open(name, O_RDONLY);
+
+       if (fd < 0)
+       {
+               sprintf(cp, "unreadable: %s", strerror(errno));
+
+               return info;
+       }
+
+       cc = read(fd, data, sizeof(data));
+
+       if (cc < 0)
+       {
+               sprintf(cp, "read error: %s", strerror(errno));
+
+               (void) close(fd);
+
+               return info;
+       }
+
+       (void) close(fd);
+
+       /*
+        * Check for an empty file.
+        */
+       if (cc == 0)
+       {
+               strcpy(cp, "empty file");
+
+               return info;
+       }
+
+       /*
+        * Check for a script file.
+        */
+       if ((cc > 2) && (data[0] == '#') && (data[1] == '!'))
+       {
+               char *  begin;
+               char *  end;
+
+               data[sizeof(data) - 1] = '\0';
+
+               begin = &data[2];
+
+               while (*begin == ' ')
+                       begin++;
+
+               end = begin;
+
+               while (*end && (*end != ' ') && (*end != '\n'))
+                       end++;
+
+               *end = '\0';
+
+               sprintf(cp, "script for \"%s\"", begin);
+
+               return info;
+       }
+
+       /*
+        * Check for special binary data types.
+        */
+       if ((data[0] == '\037') && (data[1] == '\235'))
+               return "compressed file";
+
+       if ((data[0] == '\037') && (data[1] == '\213'))
+               return "GZIP file";
+
+       if ((data[0] == '\177') && (memcmp(&data[1], "ELF", 3) == 0))
+       {
+               strcpy(cp, "ELF program");
+
+               return info;
+       }
+
+       /*
+        * Check for binary data.
+        */
+       badCount = 0;
+
+       for (i = 0; i < cc; i++)
+       {
+               ch = data[i];
+
+               if ((ch == '\n') || (ch == '\t'))
+                       continue;
+
+               if (isspace(ch) || isprint(ch))
+                       continue;
+
+               badCount++;
+       }
+
+       if (badCount != 0)
+       {
+               strcpy(cp, "binary");
+
+               return info;
+       }
+
+       /*
+        * It is just a text file.
+        */
+       strcpy(cp, "text file");
+
+       return info;
+}
+
+/* END CODE */
diff --git a/cmd_find.c b/cmd_find.c
new file mode 100644 (file)
index 0000000..63b77b3
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 1999 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * The "find" built-in command.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "sash.h"
+
+
+#ifdef S_ISLNK
+#define        LSTAT   lstat
+#else
+#define        LSTAT   stat
+#endif
+
+
+#define        MAX_NAME_SIZE   (1024 * 10)
+
+
+/*
+ * Items that can be specified to restrict the output.
+ */
+static BOOL            xdevFlag;
+static dev_t           xdevDevice;
+static long            fileSize;
+static const char *    filePattern;
+static const char *    fileType;
+
+
+/*
+ * Recursive routine to examine the files in a directory.
+ */
+static void    examineDirectory(const char * path);
+static BOOL    testFile(const char * fullName, const struct stat * statBuf);
+
+
+
+/*
+ * Find files from the specified directory path.
+ * This is limited to just printing their file names.
+ */
+void
+do_find(int argc, const char ** argv)
+{
+       const char *    cp;
+       const char *    path;
+       struct stat     statBuf;
+
+       argc--;
+       argv++;
+
+       xdevFlag = FALSE;
+       fileType = NULL;
+       filePattern = NULL;
+       fileSize = 0;
+
+       if ((argc <= 0) || (**argv == '-'))
+       {
+               fprintf(stderr, "No path specified\n");
+
+               return;
+       }
+
+       path = *argv++;
+       argc--;
+
+       while (argc > 0)
+       {
+               argc--;
+               cp = *argv++;
+
+               if (strcmp(cp, "-xdev") == 0)
+                       xdevFlag = TRUE;
+               else if (strcmp(cp, "-type") == 0)
+               {
+                       if ((argc <= 0) || (**argv == '-'))
+                       {
+                               fprintf(stderr, "Missing type string\n");
+
+                               return;
+                       }
+
+                       argc--;
+                       fileType = *argv++;
+               }
+               else if (strcmp(cp, "-name") == 0)
+               {
+                       if ((argc <= 0) || (**argv == '-'))
+                       {
+                               fprintf(stderr, "Missing file name\n");
+
+                               return;
+                       }
+
+                       argc--;
+                       filePattern = *argv++;
+               }
+               else if (strcmp(cp, "-size") == 0)
+               {
+                       if ((argc <= 0) || (**argv == '-'))
+                       {
+                               fprintf(stderr, "Missing file size\n");
+
+                               return;
+                       }
+
+                       argc--;
+                       cp = *argv++;
+
+                       fileSize = 0;
+
+                       while (isDecimal(*cp))
+                               fileSize = fileSize * 10 + (*cp++ - '0');
+
+                       if (*cp || (fileSize < 0))
+                       {
+                               fprintf(stderr, "Bad file size specified\n");
+
+                               return;
+                       }
+               }
+               else
+               {
+                       if (*cp != '-')
+                               fprintf(stderr, "Missing dash in option\n");
+                       else
+                               fprintf(stderr, "Unknown option\n");
+
+                       return;
+               }
+       }
+
+       /*
+        * Get information about the path and make sure that it
+        * is a directory.
+        */
+       if (stat(path, &statBuf) < 0)
+       {
+               fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
+                       strerror(errno));
+
+               return;
+       }
+
+       if (!S_ISDIR(statBuf.st_mode))
+       {
+               fprintf(stderr, "Path \"%s\" is not a directory\n", path);
+
+               return;
+       }
+
+       /*
+        * Remember the device that this directory is on in case we need it.
+        */
+       xdevDevice = statBuf.st_dev;
+
+       /*
+        * If the directory meets the specified criteria, then print it out.
+        */
+       if (testFile(path, &statBuf))
+               printf("%s\n", path);
+
+       /*
+        * Now examine the files in the directory.
+        */
+       examineDirectory(path);
+}
+
+
+/*
+ * Recursive routine to examine the files in a directory.
+ */
+static void
+examineDirectory(const char * path)
+{
+       DIR *           dir;
+       BOOL            needSlash;
+       struct dirent * entry;
+       struct stat     statBuf;
+       char            fullName[MAX_NAME_SIZE];
+
+       /*
+        * Open the directory.
+        */
+       dir = opendir(path);
+
+       if (dir == NULL)
+       {
+               fprintf(stderr, "Cannot read directory \"%s\": %s\n",
+                       path, strerror(errno));
+
+               return;
+       }
+
+       /*
+        * See if a slash is needed.
+        */
+       needSlash = (*path && (path[strlen(path) - 1] != '/'));
+
+       /*
+        * Read all of the directory entries and check them,
+        * except for the current and parent directory entries.
+        */
+       while (!intFlag && ((entry = readdir(dir)) != NULL))
+       {
+               if ((strcmp(entry->d_name, ".") == 0) ||
+                       (strcmp(entry->d_name, "..") == 0))
+               {
+                       continue;
+               }
+
+               /*
+                * Build the full path name.
+                */
+               strcpy(fullName, path);
+
+               if (needSlash)
+                       strcat(fullName, "/");
+
+               strcat(fullName, entry->d_name);
+
+               /*
+                * Find out about this file.
+                */
+               if (LSTAT(fullName, &statBuf) < 0)
+               {
+                       fprintf(stderr, "Cannot stat \"%s\": %s\n",
+                               fullName, strerror(errno));
+
+                       continue;
+               }
+
+               /*
+                * If this file matches the criteria that was
+                * specified then print its name out.
+                */
+               if (testFile(fullName, &statBuf))
+                       printf("%s\n", fullName);
+
+               /*
+                * If this is a directory and we are allowed to cross
+                * mount points or the directory is still on the same
+                * device, then examine it's files too.
+                */
+               if (S_ISDIR(statBuf.st_mode) &&
+                       (!xdevFlag || (statBuf.st_dev == xdevDevice)))
+               {
+                       examineDirectory(fullName);
+               }
+       }
+
+       closedir(dir);
+}
+
+
+/*
+ * Test a file name having the specified status to see if it should
+ * be acted on.  Returns TRUE if the file name has been selected.
+ */
+static BOOL
+testFile(const char * fullName, const struct stat * statBuf)
+{
+       const char *    cp;
+       const char *    entryName;
+       BOOL            wantType;
+       int             mode;
+
+       mode = statBuf->st_mode;
+
+       /*
+        * Check the file type if it was specified.
+        */
+       if (fileType != NULL)
+       {
+               wantType = FALSE;
+
+               for (cp = fileType; *cp; cp++)
+               {
+                       switch (*cp)
+                       {
+                               case 'f':
+                                       if (S_ISREG(mode))
+                                               wantType = TRUE;
+                                       break;
+
+                               case 'd':
+                                       if (S_ISDIR(mode))
+                                               wantType = TRUE;
+
+                                       break;
+
+                               case 'p':
+                                       if (S_ISFIFO(mode))
+                                               wantType = TRUE;
+
+                                       break;
+
+                               case 'c':
+                                       if (S_ISCHR(mode))
+                                               wantType = TRUE;
+
+                                       break;
+
+                               case 'b':
+                                       if (S_ISBLK(mode))
+                                               wantType = TRUE;
+
+                                       break;
+
+                               case 's':
+                                       if (S_ISSOCK(mode))
+                                               wantType = TRUE;
+
+                                       break;
+
+#ifdef S_ISLNK
+                               case 'l':
+                                       if (S_ISLNK(mode))
+                                               wantType = TRUE;
+
+                                       break;
+#endif
+                               default:
+                                       break;
+                       }
+               }
+
+               if (!wantType)
+                       return FALSE;
+       }
+
+       /*
+        * Check the file size if it was specified.
+        * This check only lets regular files and directories through.
+        */
+       if (fileSize > 0)
+       {
+               if (!S_ISREG(mode) && !S_ISDIR(mode))
+                       return FALSE;
+
+               if (statBuf->st_size < fileSize)
+                       return FALSE;
+       }
+
+       /*
+        * Check the file name pattern if it was specified.
+        */
+       if (filePattern != NULL)
+       {
+               entryName = strrchr(fullName, '/');
+
+               if (entryName)
+                       entryName++;
+               else
+                       entryName = fullName;
+
+               if (!match(entryName, filePattern))
+                       return FALSE;
+       }
+
+       /*
+        * This file name is wanted.
+        */
+       return TRUE;
+}
+
+/* END CODE */
index 6f6e95551eebbf6bd75a4f5576ca655c7fb38b75..1f10eb270db2b807d0ec1b571a9740e6921dd042 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
 
 
 static BOOL    search
-       PROTO((const char * string, const char * word, BOOL ignorecase));
+       (const char * string, const char * word, BOOL ignoreCase);
 
 
 void
-do_grep(argc, argv)
-       int             argc;
-       const char **   argv;
+do_grep(int argc, const char ** argv)
 {
        FILE *          fp;
        const char *    word;
        const char *    name;
        const char *    cp;
-       BOOL            tellname;
-       BOOL            ignorecase;
-       BOOL            tellline;
+       BOOL            tellName;
+       BOOL            ignoreCase;
+       BOOL            tellLine;
        long            line;
-       char            buf[BUFSIZE];
+       char            buf[BUF_SIZE];
 
-       ignorecase = FALSE;
-       tellline = FALSE;
+       ignoreCase = FALSE;
+       tellLine = FALSE;
 
        argc--;
        argv++;
 
-       if (**argv == '-') {
+       if (**argv == '-')
+       {
                argc--;
                cp = *argv++;
 
-               while (*++cp) switch (*cp) {
+               while (*++cp) switch (*cp)
+               {
                        case 'i':
-                               ignorecase = TRUE;
+                               ignoreCase = TRUE;
                                break;
 
                        case 'n':
-                               tellline = TRUE;
+                               tellLine = TRUE;
                                break;
 
                        default:
@@ -59,14 +59,16 @@ do_grep(argc, argv)
        word = *argv++;
        argc--;
 
-       tellname = (argc > 1);
+       tellName = (argc > 1);
 
-       while (argc-- > 0) {
+       while (argc-- > 0)
+       {
                name = *argv++;
 
                fp = fopen(name, "r");
 
-               if (fp == NULL) {
+               if (fp == NULL)
+               {
                        perror(name);
 
                        continue;
@@ -74,8 +76,10 @@ do_grep(argc, argv)
 
                line = 0;
 
-               while (fgets(buf, sizeof(buf), fp)) {
-                       if (intflag) {
+               while (fgets(buf, sizeof(buf), fp))
+               {
+                       if (intFlag)
+                       {
                                fclose(fp);
 
                                return;
@@ -88,11 +92,12 @@ do_grep(argc, argv)
                        if (*cp != '\n')
                                fprintf(stderr, "%s: Line too long\n", name);
 
-                       if (search(buf, word, ignorecase)) {
-                               if (tellname)
+                       if (search(buf, word, ignoreCase))
+                       {
+                               if (tellName)
                                        printf("%s: ", name);
 
-                               if (tellline)
+                               if (tellLine)
                                        printf("%ld: ", line);
 
                                fputs(buf, stdout);
@@ -111,22 +116,21 @@ do_grep(argc, argv)
  * See if the specified word is found in the specified string.
  */
 static BOOL
-search(string, word, ignorecase)
-       const char *    string;
-       const char *    word;
-       BOOL            ignorecase;
+search(const char * string, const char * word, BOOL ignoreCase)
 {
        const char *    cp1;
        const char *    cp2;
        int             len;
-       int             lowfirst;
+       int             lowFirst;
        int             ch1;
        int             ch2;
 
        len = strlen(word);
 
-       if (!ignorecase) {
-               while (TRUE) {
+       if (!ignoreCase)
+       {
+               while (TRUE)
+               {
                        string = strchr(string, word[0]);
 
                        if (string == NULL)
@@ -143,14 +147,15 @@ search(string, word, ignorecase)
         * Here if we need to check case independence.
         * Do the search by lower casing both strings.
         */
-       lowfirst = *word;
+       lowFirst = *word;
 
-       if (isupper(lowfirst))
-               lowfirst = tolower(lowfirst);
+       if (isupper(lowFirst))
+               lowFirst = tolower(lowFirst);
 
-       while (TRUE) {
-               while (*string && (*string != lowfirst) &&
-                       (!isupper(*string) || (tolower(*string) != lowfirst)))
+       while (TRUE)
+       {
+               while (*string && (*string != lowFirst) &&
+                       (!isupper(*string) || (tolower(*string) != lowFirst)))
                {
                        string++;
                }
@@ -161,7 +166,8 @@ search(string, word, ignorecase)
                cp1 = string;
                cp2 = word;
 
-               do {
+               do
+               {
                        if (*cp2 == '\0')
                                return TRUE;
 
@@ -175,7 +181,8 @@ search(string, word, ignorecase)
                        if (isupper(ch2))
                                ch2 = tolower(ch2);
 
-               } while (ch1 == ch2);
+               }
+               while (ch1 == ch2);
 
                string++;
        }
index f123fd7d714db661dd52ee86d4d43b14349d120e..617984d6370486a02938a9526cc7f13da9686374 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
@@ -55,17 +55,15 @@ static const CONVERT        gunzipConvertTable[] =
 /*
  * Local routines to compress and uncompress files.
  */
-static BOOL    gzip PROTO((const char * inputFile, const char * outputFile));
-static BOOL    gunzip PROTO((const char * inputFile, const char * outputFile));
+static BOOL    gzip(const char * inputFile, const char * outputFile);
+static BOOL    gunzip(const char * inputFile, const char * outputFile);
 
 static const char * convertName
-       PROTO((const CONVERT * table, const char * inFile));
+       (const CONVERT * table, const char * inFile);
 
 
 void
-do_gzip(argc, argv)
-       int             argc;
-       const char **   argv;
+do_gzip(int argc, const char ** argv)
 {
        const char *    outPath;
        const char *    inFile;
@@ -112,7 +110,7 @@ do_gzip(argc, argv)
         */
        if (outPath == NULL)
        {
-               while (!intflag && (argc-- > 0))
+               while (!intFlag && (argc-- > 0))
                {
                        inFile = *argv++;
 
@@ -144,7 +142,7 @@ do_gzip(argc, argv)
         * specified input file to the exactly specified output path,
         * or else complain.
         */
-       if (!isadir(outPath))
+       if (!isDirectory(outPath))
        {
                if (argc == 1)
                        (void) gzip(*argv, outPath);
@@ -159,7 +157,7 @@ do_gzip(argc, argv)
         * Compress each of the input files into the specified
         * output directory, converting their extensions if possible.
         */
-       while (!intflag && (argc-- > 0))
+       while (!intFlag && (argc-- > 0))
        {
                inFile = *argv++;
 
@@ -184,7 +182,7 @@ do_gzip(argc, argv)
                 * Now build the output path name by prefixing it with
                 * the output directory.
                 */
-               outFile = buildname(outPath, outFile);
+               outFile = buildName(outPath, outFile);
 
                /*
                 * Compress the input file without deleting the input file.
@@ -195,9 +193,7 @@ do_gzip(argc, argv)
 
 
 void
-do_gunzip(argc, argv)
-       int             argc;
-       const char **   argv;
+do_gunzip(int argc, const char ** argv)
 {
        const char *    outPath;
        const char *    inFile;
@@ -245,7 +241,7 @@ do_gunzip(argc, argv)
         */
        if (outPath == NULL)
        {
-               while (!intflag && (argc-- > 0))
+               while (!intFlag && (argc-- > 0))
                {
                        inFile = *argv++;
 
@@ -281,11 +277,23 @@ do_gunzip(argc, argv)
 
        /*
         * There is an output path specified.
-        * If it is not a directory, then either uncompress the single
-        * specified input file to the exactly specified output path,
+        * If the output path is a device file then uncompress each of
+        * the input files to the device file.
+        */
+       if (isDevice(outPath))
+       {
+               while (!intFlag && (argc-- > 0))
+                       (void) gunzip(*argv++, outPath);
+
+               return;
+       }
+
+       /*
+        * If the output path is not a directory then either uncompress the
+        * single specified input file to the exactly specified output path,
         * or else complain.
         */
-       if (!isadir(outPath))
+       if (!isDirectory(outPath))
        {
                if (argc == 1)
                        (void) gunzip(*argv, outPath);
@@ -300,7 +308,7 @@ do_gunzip(argc, argv)
         * Uncompress each of the input files into the specified
         * output directory, converting their extensions if possible.
         */
-       while (!intflag && (argc-- > 0))
+       while (!intFlag && (argc-- > 0))
        {
                inFile = *argv++;
 
@@ -325,7 +333,7 @@ do_gunzip(argc, argv)
                 * Now build the output path name by prefixing it with
                 * the output directory.
                 */
-               outFile = buildname(outPath, outFile);
+               outFile = buildName(outPath, outFile);
 
                /*
                 * Uncompress the input file without deleting the input file.
@@ -340,17 +348,15 @@ do_gunzip(argc, argv)
  * Returns TRUE if successful.
  */
 static BOOL
-gzip(inputFileName, outputFileName)
-       const char *    inputFileName;
-       const char *    outputFileName;
+gzip(const char * inputFileName, const char * outputFileName)
 {
        gzFile          outGZ;
        int             inFD;
        int             len;
        int             err;
-       struct  stat    statbuf1;
-       struct  stat    statbuf2;
-       char            buf[BUFSIZE];
+       struct  stat    statBuf1;
+       struct  stat    statBuf2;
+       char            buf[BUF_SIZE];
 
        outGZ = NULL;
        inFD = -1;
@@ -359,19 +365,21 @@ gzip(inputFileName, outputFileName)
         * See if the output file is the same as the input file.
         * If so, complain about it.
         */
-       if (stat(inputFileName, &statbuf1) < 0) {
+       if (stat(inputFileName, &statBuf1) < 0)
+       {
                perror(inputFileName);
 
                return FALSE;
        }
 
-       if (stat(outputFileName, &statbuf2) < 0) {
-               statbuf2.st_ino = -1;
-               statbuf2.st_dev = -1;
+       if (stat(outputFileName, &statBuf2) < 0)
+       {
+               statBuf2.st_ino = -1;
+               statBuf2.st_dev = -1;
        }
 
-       if ((statbuf1.st_dev == statbuf2.st_dev) &&
-               (statbuf1.st_ino == statbuf2.st_ino))
+       if ((statBuf1.st_dev == statBuf2.st_dev) &&
+               (statBuf1.st_ino == statBuf2.st_ino))
        {
                fprintf(stderr,
                        "Cannot compress file \"%s\" on top of itself\n",
@@ -385,7 +393,8 @@ gzip(inputFileName, outputFileName)
         */
        inFD = open(inputFileName, O_RDONLY);
 
-       if (inFD < 0) {
+       if (inFD < 0)
+       {
                perror(inputFileName);
 
                goto failed;
@@ -396,7 +405,8 @@ gzip(inputFileName, outputFileName)
         */
        outGZ = gzopen(outputFileName, "wb9");
 
-       if (outGZ == NULL) {
+       if (outGZ == NULL)
+       {
                fprintf(stderr, "%s: gzopen failed\n", outputFileName);
 
                goto failed;
@@ -406,19 +416,22 @@ gzip(inputFileName, outputFileName)
         * Read the uncompressed data from the input file and write
         * the compressed data to the output file.
         */
-       while ((len = read(inFD, buf, sizeof(buf))) > 0) {
-               if (gzwrite(outGZ, buf, len) != len) {
+       while ((len = read(inFD, buf, sizeof(buf))) > 0)
+       {
+               if (gzwrite(outGZ, buf, len) != len)
+               {
                        fprintf(stderr, "%s: %s\n", inputFileName,
                                gzerror(outGZ, &err));
 
                        goto failed;
                }
 
-               if (intflag)
+               if (intFlag)
                        goto failed;
        }
 
-       if (len < 0) {
+       if (len < 0)
+       {
                perror(inputFileName);
 
                goto failed;
@@ -427,7 +440,8 @@ gzip(inputFileName, outputFileName)
        /*
         * All done, close the files.
         */
-       if (close(inFD)) {
+       if (close(inFD))
+       {
                perror(inputFileName);
 
                goto failed;
@@ -435,7 +449,8 @@ gzip(inputFileName, outputFileName)
 
        inFD = -1;
 
-       if (gzclose(outGZ) != Z_OK) {
+       if (gzclose(outGZ) != Z_OK)
+       {
                fprintf(stderr, "%s: gzclose failed\n", outputFileName);
 
                goto failed;
@@ -468,17 +483,15 @@ failed:
  * Returns TRUE if successful.
  */
 static BOOL
-gunzip(inputFileName, outputFileName)
-       const char *    inputFileName;
-       const char *    outputFileName;
+gunzip(const char * inputFileName, const char * outputFileName)
 {
        gzFile          inGZ;
        int             outFD;
        int             len;
        int             err;
-       struct  stat    statbuf1;
-       struct  stat    statbuf2;
-       char            buf[BUFSIZE];
+       struct  stat    statBuf1;
+       struct  stat    statBuf2;
+       char            buf[BUF_SIZE];
 
        inGZ = NULL;
        outFD = -1;
@@ -487,19 +500,21 @@ gunzip(inputFileName, outputFileName)
         * See if the output file is the same as the input file.
         * If so, complain about it.
         */
-       if (stat(inputFileName, &statbuf1) < 0) {
+       if (stat(inputFileName, &statBuf1) < 0)
+       {
                perror(inputFileName);
 
                return FALSE;
        }
 
-       if (stat(outputFileName, &statbuf2) < 0) {
-               statbuf2.st_ino = -1;
-               statbuf2.st_dev = -1;
+       if (stat(outputFileName, &statBuf2) < 0)
+       {
+               statBuf2.st_ino = -1;
+               statBuf2.st_dev = -1;
        }
 
-       if ((statbuf1.st_dev == statbuf2.st_dev) &&
-               (statbuf1.st_ino == statbuf2.st_ino))
+       if ((statBuf1.st_dev == statBuf2.st_dev) &&
+               (statBuf1.st_ino == statBuf2.st_ino))
        {
                fprintf(stderr,
                        "Cannot uncompress file \"%s\" on top of itself\n",
@@ -513,7 +528,8 @@ gunzip(inputFileName, outputFileName)
         */
        inGZ = gzopen(inputFileName, "rb");
 
-       if (inGZ == NULL) {
+       if (inGZ == NULL)
+       {
                fprintf(stderr, "%s: gzopen failed\n", inputFileName);
 
                return FALSE;
@@ -522,9 +538,13 @@ gunzip(inputFileName, outputFileName)
        /*
         * Create the output file.
         */
-       outFD = open(outputFileName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       if (isDevice(outputFileName))
+               outFD = open(outputFileName, O_WRONLY);
+       else
+               outFD = open(outputFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666);
 
-       if (outFD < 0) {
+       if (outFD < 0)
+       {
                perror(outputFileName);
 
                goto failed;
@@ -534,18 +554,21 @@ gunzip(inputFileName, outputFileName)
         * Read the compressed data from the input file and write
         * the uncompressed data extracted from it to the output file.
         */
-       while ((len = gzread(inGZ, buf, sizeof(buf))) > 0) {
-               if (fullWrite(outFD, buf, len) < 0) {
+       while ((len = gzread(inGZ, buf, sizeof(buf))) > 0)
+       {
+               if (fullWrite(outFD, buf, len) < 0)
+               {
                        perror(outputFileName);
 
                        goto failed;
                }
 
-               if (intflag)
+               if (intFlag)
                        goto failed;
        }
 
-       if (len < 0) {
+       if (len < 0)
+       {
                fprintf(stderr, "%s: %s\n", inputFileName,
                        gzerror(inGZ, &err));
 
@@ -555,7 +578,8 @@ gunzip(inputFileName, outputFileName)
        /*
         * All done, close the files.
         */
-       if (close(outFD)) {
+       if (close(outFD))
+       {
                perror(outputFileName);
 
                goto failed;
@@ -563,7 +587,8 @@ gunzip(inputFileName, outputFileName)
 
        outFD = -1;
 
-       if (gzclose(inGZ) != Z_OK) {
+       if (gzclose(inGZ) != Z_OK)
+       {
                fprintf(stderr, "%s: gzclose failed\n", inputFileName);
 
                goto failed;
@@ -600,13 +625,11 @@ failed:
  * returned.
  */
 const char *
-convertName(table, inputFile)
-       const CONVERT * table;
-       const char *    inputFile;
+convertName(const CONVERT * table, const char * inputFile)
 {
        int             inputLength;
        int             testLength;
-       static char     buf[PATHLEN];
+       static char     buf[PATH_LEN];
 
        inputLength = strlen(inputFile);
 
index 348b2958b9c4d58b6b84dacb9c4adca4690009ad..ab8d8db759d0196f10266236d387a6876f9b7789 100644 (file)
--- a/cmd_ls.c
+++ b/cmd_ls.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
@@ -15,7 +15,7 @@
 #include <grp.h>
 
 
-#define        LISTSIZE        256
+#define        LISTSIZE        8192
 
 
 #ifdef S_ISLNK
 #define        LSF_DIR         0x02
 #define        LSF_INODE       0x04
 #define        LSF_MULT        0x08
+#define        LSF_FLAG        0x10
+#define        LSF_COLUMN      0x20
 
 
+/*
+ * Data holding list of files.
+ */
 static char ** list;
-static int     listsize;
-static int     listused;
+static int     listSize;
+static int     listUsed;
 
 
-static void    lsfile
-       PROTO((const char * name, const struct stat * statbuf, int flags));
+/*
+ * Local procedures.
+ */
+static void    listFile(
+       const char *            name,
+       const struct stat *     statBuf,
+       int                     flags,
+       int                     width
+);
+
+static BOOL    addListName(const char * fileName);
+static void    listAllFiles(int flags, int displayWidth);
+static void    clearListNames(void);
 
 
 void
-do_ls(argc, argv)
-       int             argc;
-       const char **   argv;
+do_ls(int argc, const char ** argv)
 {
        const char *    cp;
        const char *    name;
        int             flags;
        int             i;
+       int             displayWidth;
+       BOOL            endSlash;
        DIR *           dirp;
-       BOOL            endslash;
-       char **         newlist;
        struct dirent * dp;
-       char            fullname[PATHLEN];
-       struct  stat    statbuf;
-
-       static const char *     def[2] = {"-ls", "."};
+       char            fullName[PATH_LEN];
+       struct  stat    statBuf;
 
-       if (listsize == 0) {
-               list = (char **) malloc(LISTSIZE * sizeof(char *));
+       static const char *     def[] = {"."};
 
-               if (list == NULL) {
-                       fprintf(stderr, "No memory for ls buffer\n");
-
-                       return;
-               }
-
-               listsize = LISTSIZE;
-       }
-
-       listused = 0;
+       /*
+        * Reset for a new listing run.
+        */
+       clearListNames();
 
+       displayWidth = 0;
        flags = 0;
 
-       if ((argc > 1) && (argv[1][0] == '-'))
+       /*
+        * Handle options.
+        */
+       argc--;
+       argv++;
+
+       while ((argc > 0) && (**argv == '-'))
        {
+               cp = *argv++ + 1;
                argc--;
-               cp = *(++argv) + 1;
 
-               while (*cp) switch (*cp++) {
+               while (*cp) switch (*cp++)
+               {
                        case 'l':       flags |= LSF_LONG; break;
                        case 'd':       flags |= LSF_DIR; break;
                        case 'i':       flags |= LSF_INODE; break;
+                       case 'F':       flags |= LSF_FLAG; break;
+                       case 'C':       flags |= LSF_COLUMN; break;
+
                        default:
                                fprintf(stderr, "Unknown option -%c\n", cp[-1]);
 
@@ -93,36 +109,94 @@ do_ls(argc, argv)
                }
        }
 
-       if (argc <= 1) {
-               argc = 2;
+       /*
+        * If long listing is specified then turn off column listing.
+        */
+       if (flags & LSF_LONG)
+               flags &= ~LSF_COLUMN;
+
+       /*
+        * If column listing is specified then calculate the maximum
+        * width available for the columns of file names.
+        * This is settable using the COLS environment variable.
+        */
+       if (flags & LSF_COLUMN)
+       {
+               name = getenv("COLS");
+
+               if (name)
+                       displayWidth = atoi(name);
+
+               if (displayWidth <= 0)
+                       displayWidth = 80;
+       }
+
+       /*
+        * If no arguments are given set up to show the current directory.
+        */
+       if (argc <= 0)
+       {
+               argc = 1;
                argv = def;
        }
 
-       if (argc > 2)
+       if (argc > 1)
                flags |= LSF_MULT;
 
-       while (argc-- > 1) {
-               name = *(++argv);
-               endslash = (*name && (name[strlen(name) - 1] == '/'));
+       /*
+        * Make one pass over the file names to collect together
+        * all of the files which are not directories.
+        * We will process them all as one list.
+        */
+       for (i = 0; i < argc; i++)
+       {
+               if ((flags & LSF_DIR) || !isDirectory(argv[i]))
+               {
+                       if (!addListName(argv[i]))
+                               return;
+               }
+       }
+
+       /*
+        * List those file names, and then clear the list.
+        */
+       listAllFiles(flags, displayWidth);
+       clearListNames();
+
+       /*
+        * If directories were being listed as themselves, then we are done.
+        */
+       if (flags & LSF_DIR)
+               return;
+
+       /*
+        * Now iterate over the file names processing the directories.
+        */
+       while (!intFlag && (argc-- > 0))
+       {
+               name = *argv++;
+               endSlash = (*name && (name[strlen(name) - 1] == '/'));
 
-               if (LSTAT(name, &statbuf) < 0) {
+               if (LSTAT(name, &statBuf) < 0)
+               {
                        perror(name);
 
                        continue;
                }
 
-               if ((flags & LSF_DIR) || (!S_ISDIR(statbuf.st_mode))) {
-                       lsfile(name, &statbuf, flags);
-
+               /*
+                * If this file name is not a directory, then ignore it.
+                */
+               if (!S_ISDIR(statBuf.st_mode))
                        continue;
-               }
 
                /*
-                * Do all the files in a directory.
+                * Collect all the files in the directory.
                 */
                dirp = opendir(name);
 
-               if (dirp == NULL) {
+               if (dirp == NULL)
+               {
                        perror(name);
 
                        continue;
@@ -131,172 +205,374 @@ do_ls(argc, argv)
                if (flags & LSF_MULT)
                        printf("\n%s:\n", name);
 
-               while ((dp = readdir(dirp)) != NULL) {
-                       if (intflag)
-                               break;
+               while (!intFlag && ((dp = readdir(dirp)) != NULL))
+               {
+                       fullName[0] = '\0';
 
-                       fullname[0] = '\0';
+                       if ((*name != '.') || (name[1] != '\0'))
+                       {
+                               strcpy(fullName, name);
 
-                       if ((*name != '.') || (name[1] != '\0')) {
-                               strcpy(fullname, name);
-                               if (!endslash)
-                                       strcat(fullname, "/");
+                               if (!endSlash)
+                                       strcat(fullName, "/");
                        }
 
-                       strcat(fullname, dp->d_name);
+                       strcat(fullName, dp->d_name);
 
-                       if (listused >= listsize) {
-                               newlist = realloc(list,
-                                       ((sizeof(char **)) * (listsize + LISTSIZE)));
+                       /*
+                        * Save the file name in the list.
+                        */
+                       if (!addListName(fullName))
+                       {
+                               closedir(dirp);
 
-                               if (newlist == NULL) {
-                                       fprintf(stderr, "No memory for ls buffer\n");
-                                       break;
-                               }
-
-                               list = newlist;
-                               listsize += LISTSIZE;
-                       }
-
-                       list[listused] = strdup(fullname);
-
-                       if (list[listused] == NULL) {
-                               fprintf(stderr, "No memory for filenames\n");
-                               break;
+                               return;
                        }
-
-                       listused++;
                }
 
                closedir(dirp);
 
                /*
-                * Sort the files.
+                * List the files we collected in this directory,
+                * and then clear the list.
                 */
-               qsort((void *) list, listused, sizeof(char *), namesort);
+               listAllFiles(flags, displayWidth);
+               clearListNames();
+       }
+}
 
-               /*
-                * Now finally list the filenames.
-                */
-               for (i = 0; i < listused; i++) {
-                       name = list[i];
 
-                       if (LSTAT(name, &statbuf) < 0) {
-                               perror(name);
-                               free((char *) name);
+/*
+ * List all of the files in the current list of files.
+ * The files are displayed according to the specified flags,
+ * in the specified display width.
+ */
+static void
+listAllFiles(int flags, int displayWidth)
+{
+       const char *    name;
+       const char *    cp;
+       int             fileWidth;
+       int             column;
+       int             len;
+       int             i;
+       struct stat     statBuf;
+
+       /*
+        * Initialise width data until we need it.
+        */
+       fileWidth = 0;
+       column = 0;
+
+       /*
+        * Sort the files in the list.
+        */
+       qsort((void *) list, listUsed, sizeof(char *), nameSort);
+
+       /*
+        * If we are showing the files in columns then calculate the
+        * maximum width of all of the file names, taking into account
+        * various factors.
+        */
+       if (flags & LSF_COLUMN)
+       {
+               for (i = 0; i < listUsed; i++)
+               {
+                       len = strlen(list[i]);
+
+                       if (fileWidth < len)
+                               fileWidth = len;
+               }
+
+               if (flags & LSF_FLAG)
+                       fileWidth++;
 
-                               continue;
-                       }
+               if (flags & LSF_INODE)
+                       fileWidth += 8;
 
-                       cp = strrchr(name, '/');
+               fileWidth += 2;
+       }
 
-                       if (cp)
-                               cp++;
-                       else
-                               cp = name;
+       /*
+        * Now list the fileNames.
+        */
+       for (i = 0; i < listUsed; i++)
+       {
+               name = list[i];
 
-                       lsfile(cp, &statbuf, flags);
+               if (LSTAT(name, &statBuf) < 0)
+               {
+                       perror(name);
 
-                       free((char *) name);
+                       continue;
                }
 
-               listused = 0;
+               cp = strrchr(name, '/');
+
+               if (cp)
+                       cp++;
+               else
+                       cp = name;
+
+               /*
+                * List the file in the next column or at the end
+                * of a line depending on the width left.
+                */
+               if (column + fileWidth * 2 >= displayWidth)
+               {
+                       listFile(cp, &statBuf, flags, 0);
+                       column = 0;
+               }
+               else
+               {
+                       listFile(cp, &statBuf, flags, fileWidth);
+                       column += fileWidth;
+               }
        }
+
+       /*
+        * Terminate the last file name if necessary.
+        */
+       if (column > 0)
+               fputc('\n', stdout);
 }
 
 
 /*
- * Do an LS of a particular file name according to the flags.
+ * Do a listing of a particular file name according to the flags.
+ * The output is shown within the specified width if it is nonzero,
+ * or on its own line if the width is zero.
  */
 static void
-lsfile(name, statbuf, flags)
-       const char *            name;
-       const struct stat *     statbuf;
-       int                     flags;
+listFile(
+       const char *            name,
+       const struct stat *     statBuf,
+       int                     flags,
+       int                     width
+)
 {
        char *          cp;
        struct passwd * pwd;
        struct group *  grp;
        int             len;
-       char            buf[PATHLEN];
-       static  char    username[12];
-       static  int     userid;
-       static  BOOL    useridknown;
-       static  char    groupname[12];
-       static  int     groupid;
-       static  BOOL    groupidknown;
-
+       int             mode;
+       int             flagChar;
+       int             usedWidth;
+       char            buf[PATH_LEN];
+       static  char    userName[12];
+       static  int     userId;
+       static  BOOL    userIdKnown;
+       static  char    groupName[12];
+       static  int     groupId;
+       static  BOOL    groupIdKnown;
+
+       mode = statBuf->st_mode;
+
+       /*
+        * Initialise buffers for use.
+        */
        cp = buf;
-       *cp = '\0';
+       buf[0] = '\0';
+       flagChar = '\0';
 
-       if (flags & LSF_INODE) {
-               sprintf(cp, "%5ld ", statbuf->st_ino);
+       /*
+        * Show the inode number if requested.
+        */
+       if (flags & LSF_INODE)
+       {
+               sprintf(cp, "%7ld ", statBuf->st_ino);
                cp += strlen(cp);
        }
 
-       if (flags & LSF_LONG) {
-               strcpy(cp, modestring(statbuf->st_mode));
+       /*
+        * Create the long status line if requested.
+        */
+       if (flags & LSF_LONG)
+       {
+               strcpy(cp, modeString(mode));
                cp += strlen(cp);
 
-               sprintf(cp, "%3d ", statbuf->st_nlink);
+               sprintf(cp, "%3d ", statBuf->st_nlink);
                cp += strlen(cp);
 
-               if (!useridknown || (statbuf->st_uid != userid)) {
-                       pwd = getpwuid(statbuf->st_uid);
+               if (!userIdKnown || (statBuf->st_uid != userId))
+               {
+                       pwd = getpwuid(statBuf->st_uid);
 
                        if (pwd)
-                               strcpy(username, pwd->pw_name);
+                               strcpy(userName, pwd->pw_name);
                        else
-                               sprintf(username, "%d", statbuf->st_uid);
+                               sprintf(userName, "%d", statBuf->st_uid);
 
-                       userid = statbuf->st_uid;
-                       useridknown = TRUE;
+                       userId = statBuf->st_uid;
+                       userIdKnown = TRUE;
                }
 
-               sprintf(cp, "%-8s ", username);
+               sprintf(cp, "%-8s ", userName);
                cp += strlen(cp);
 
-               if (!groupidknown || (statbuf->st_gid != groupid)) {
-                       grp = getgrgid(statbuf->st_gid);
+               if (!groupIdKnown || (statBuf->st_gid != groupId))
+               {
+                       grp = getgrgid(statBuf->st_gid);
 
                        if (grp)
-                               strcpy(groupname, grp->gr_name);
+                               strcpy(groupName, grp->gr_name);
                        else
-                               sprintf(groupname, "%d", statbuf->st_gid);
+                               sprintf(groupName, "%d", statBuf->st_gid);
 
-                       groupid = statbuf->st_gid;
-                       groupidknown = TRUE;
+                       groupId = statBuf->st_gid;
+                       groupIdKnown = TRUE;
                }
 
-               sprintf(cp, "%-8s ", groupname);
+               sprintf(cp, "%-8s ", groupName);
                cp += strlen(cp);
 
-               if (S_ISBLK(statbuf->st_mode) || S_ISCHR(statbuf->st_mode))
-                       sprintf(cp, "%3d, %3d ", statbuf->st_rdev >> 8,
-                               statbuf->st_rdev & 0xff);
+               if (S_ISBLK(mode) || S_ISCHR(mode))
+               {
+                       sprintf(cp, "%3lu, %3lu ",
+                               ((unsigned long) statBuf->st_rdev) >> 8,
+                               ((unsigned long) statBuf->st_rdev) & 0xff);
+               }
                else
-                       sprintf(cp, "%8ld ", statbuf->st_size);
+                       sprintf(cp, "%8ld ", statBuf->st_size);
 
                cp += strlen(cp);
 
-               sprintf(cp, " %-12s ", timestring(statbuf->st_mtime));
+               sprintf(cp, " %-12s ", timeString(statBuf->st_mtime));
+       }
+
+       /*
+        * Set the special character if the file is a directory or
+        * symbolic link or executable and the display was requested.
+        */
+       if (flags & LSF_FLAG)
+       {
+               if (S_ISDIR(mode))
+                       flagChar = '/';
+#ifdef S_ISLNK
+               else if (S_ISLNK(mode))
+                       flagChar = '@';
+#endif
+               else if ((mode & 0111) != 0)
+                       flagChar = '*';
        }
 
+       /*
+        * Print the status info followed by the file name.
+        */
        fputs(buf, stdout);
        fputs(name, stdout);
 
+       if (flagChar)
+               fputc(flagChar, stdout);
+
+       /*
+        * Calculate the width used so far.
+        */
+       usedWidth = strlen(buf) + strlen(name);
+
+       if (flagChar)
+               usedWidth++;
+
+       /*
+        * Show where a symbolic link points.
+        */
 #ifdef S_ISLNK
-       if ((flags & LSF_LONG) && S_ISLNK(statbuf->st_mode)) {
-               len = readlink(name, buf, PATHLEN - 1);
+       if ((flags & LSF_LONG) && S_ISLNK(mode))
+       {
+               len = readlink(name, buf, PATH_LEN - 1);
 
-               if (len >= 0) {
+               if (len >= 0)
+               {
                        buf[len] = '\0';
                        printf(" -> %s", buf);
                }
+
+               usedWidth += strlen(buf) + 4;
        }
 #endif
 
-       fputc('\n', stdout);
+       /*
+        * If no width was given then just end the line with a newline.
+        */
+       if (width == 0)
+       {
+               fputc('\n', stdout);
+
+               return;
+       }
+
+       /*
+        * There is a width given.
+        * Print as many spaces as it takes to reach that width.
+        */
+       while (usedWidth++ < width)
+               fputc(' ', stdout);
+}
+
+
+/*
+ * Save a file name to the end of the static list, reallocating if necessary.
+ * The file name is copied into allocated memory owned by the list.
+ * Returns TRUE on success.
+ */
+static BOOL
+addListName(const char * fileName)
+{
+       char ** newList;
+
+       /*
+        * Reallocate the list if necessary.
+        */
+       if (listUsed >= listSize)
+       {
+               newList = realloc(list,
+                       ((sizeof(char **)) * (listSize + LISTSIZE)));
+
+               if (newList == NULL)
+               {
+                       fprintf(stderr, "No memory for file name buffer\n");
+
+                       return FALSE;
+               }
+
+               list = newList;
+               listSize += LISTSIZE;
+       }
+
+       /*
+        * Copy the file name into the next entry.
+        */
+       list[listUsed] = strdup(fileName);
+
+       if (list[listUsed] == NULL)
+       {
+               fprintf(stderr, "No memory for file name\n");
+
+               return FALSE;
+       }
+
+       /*
+        * Increment the amount of space used.
+        */
+       listUsed++;
+
+       return TRUE;
+}
+
+
+/*
+ * Free all of the names from the list of file names.
+ */
+static void
+clearListNames(void)
+{
+       while (listUsed > 0)
+       {
+               listUsed--;
+
+               free(list[listUsed]);
+       }
 }
 
 /* END CODE */
index 69f50ac8a2a700fd8f96c3972b5445d527f0e5f8..c2f240a3d5c09f0728d0796f10b0375959aa2b59 100644 (file)
--- a/cmd_tar.c
+++ b/cmd_tar.c
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
  * The "tar" built-in command.
+ * 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>
 
 
 /*
- * Tar file format.
+ * Tar file constants.
  */
-#define TBLOCK 512
-#define NAMSIZ 100
-
-union hblock {
-       char dummy[TBLOCK];
-       struct header {
-               char name[NAMSIZ];
-               char mode[8];
-               char uid[8];
-               char gid[8];
-               char size[12];
-               char mtime[12];
-               char chksum[8];
-               char linkflag;
-               char linkname[NAMSIZ];
-               char extno[4];
-               char extotal[4];
-               char efsize[12];
-       } dbuf;
-} dblock;
-
-
-static BOOL    inheader;
-static BOOL    badheader;
-static BOOL    badwrite;
-static BOOL    extracting;
-static BOOL    warnedroot;
-static BOOL    eof;
-static BOOL    verbose;
-static long    datacc;
-static int     outfd;
-static char    outname[NAMSIZ];
-
-
-static void    doheader PROTO((const struct header * hp));
-static void    dodata PROTO((const char * cp, int count));
-static void    createpath PROTO((const char * name, int mode));
-static long    getoctal PROTO((const char * cp, int len));
+#define TAR_BLOCK_SIZE 512
+#define TAR_NAME_SIZE  100
+
+
+/*
+ * The POSIX (and basic GNU) tar header format.
+ * This structure is always embedded in a TAR_BLOCK_SIZE sized block
+ * with zero padding.  We only process this information minimally.
+ */
+typedef struct
+{
+       char    name[TAR_NAME_SIZE];
+       char    mode[8];
+       char    uid[8];
+       char    gid[8];
+       char    size[12];
+       char    mtime[12];
+       char    checkSum[8];
+       char    typeFlag;
+       char    linkName[TAR_NAME_SIZE];
+       char    magic[6];
+       char    version[2];
+       char    uname[32];
+       char    gname[32];
+       char    devMajor[8];
+       char    devMinor[8];
+       char    prefix[155];
+} TarHeader;
+
+
+#define        TAR_MAGIC       "ustar"
+#define        TAR_VERSION     "00"
+
+#define        TAR_TYPE_REGULAR        '0'
+#define        TAR_TYPE_HARD_LINK      '1'
+#define        TAR_TYPE_SOFT_LINK      '2'
+
+
+/*
+ * Static data.
+ */
+static BOOL            listFlag;
+static BOOL            extractFlag;
+static BOOL            createFlag;
+static BOOL            verboseFlag;
+
+static BOOL            inHeader;
+static BOOL            badHeader;
+static BOOL            errorFlag;
+static BOOL            skipFileFlag;
+static BOOL            warnedRoot;
+static BOOL            eofFlag;
+static long            dataCc;
+static int             outFd;
+static char            outName[TAR_NAME_SIZE];
+
+
+/*
+ * Static data associated with the tar file.
+ */
+static const char *    tarName;
+static int             tarFd;
+static dev_t           tarDev;
+static ino_t           tarInode;
+
+
+/*
+ * Local procedures to restore files from a tar file.
+ */
+static void    readTarFile(int fileCount, const char ** 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);
+
+
+/*
+ * Local procedures to save files into a tar file.
+ */
+static void    saveFile(const char * fileName, BOOL seeLinks);
+
+static void    saveRegularFile(const char * fileName,
+                       const struct stat * statbuf);
+
+static void    saveDirectory(const char * fileName,
+                       const struct stat * statbuf);
+
+static BOOL    wantFileName(const char * fileName,
+                       int fileCount, const char ** fileTable);
+
+static void    writeHeader(const char * fileName,
+                       const struct stat * statbuf);
+
+static void    writeTarFile(int fileCount, const char ** fileTable);
+static void    writeTarBlock(const char * buf, int len);
+static BOOL    putOctal(char * cp, int len, long value);
+
 
 
 void
-do_tar(argc, argv)
-       int             argc;
-       const char **   argv;
+do_tar(int argc, const char ** argv)
 {
-       const char *    str;
-       const char *    devname;
-       const char *    cp;
-       int             devfd;
-       int             cc;
-       long            incc;
-       int             blocksize;
-       BOOL            createflag;
-       BOOL            listflag;
-       BOOL            fileflag;
-       char            buf[BUFSIZE];
-
-       if (argc < 2) {
+       const char *    options;
+
+       argc--;
+       argv++;
+
+       if (argc < 2)
+       {
                fprintf(stderr, "Too few arguments for tar\n");
 
                return;
        }
 
-       createflag = FALSE;
-       extracting = FALSE;
-       listflag = FALSE;
-       fileflag = FALSE;
-       verbose = FALSE;
-       badwrite = FALSE;
-       badheader = FALSE;
-       warnedroot = FALSE;
-       eof = FALSE;
-       inheader = TRUE;
-       incc = 0;
-       datacc = 0;
-       outfd = -1;
-       blocksize = sizeof(buf);
-
-       for (str = argv[1]; *str; str++) {
-               switch (*str) {
-                       case 'f':       fileflag = TRUE; break;
-                       case 't':       listflag = TRUE; break;
-                       case 'x':       extracting = TRUE; break;
-                       case 'v':       verbose = TRUE; break;
+       extractFlag = FALSE;
+       createFlag = FALSE;
+       listFlag = FALSE;
+       verboseFlag = FALSE;
+       tarName = NULL;
+       tarDev = 0;
+       tarInode = 0;
+       tarFd = -1;
+
+       /*
+        * Parse the options.
+        */
+       options = *argv++;
+       argc--;
+
+       for (; *options; options++)
+       {
+               switch (*options)
+               {
+                       case 'f':
+                               if (tarName != NULL)
+                               {
+                                       fprintf(stderr, "Only one 'f' option allowed\n");
+
+                                       return;
+                               }
+
+                               tarName = *argv++;
+                               argc--;
+
+                               break;
+
+                       case 't':
+                               listFlag = TRUE;
+                               break;
+
+                       case 'x':
+                               extractFlag = TRUE;
+                               break;
 
                        case 'c':
-                       case 'a':
-                               fprintf(stderr, "Writing is not supported\n");
+                               createFlag = TRUE;
+                               break;
 
-                               return;
+                       case 'v':
+                               verboseFlag = TRUE;
+                               break;
 
                        default:
-                               fprintf(stderr, "Unknown tar flag\n");
+                               fprintf(stderr, "Unknown tar flag '%c'\n", *options);
 
                                return;
                }
        }
 
-       if (!fileflag) {
-               fprintf(stderr, "The 'f' flag must be specified\n");
+       /*
+        * Validate the options.
+        */
+       if (extractFlag + listFlag + createFlag != 1)
+       {
+               fprintf(stderr, "Exactly one of 'c', 'x' or 't' must be specified\n");
 
                return;
        }
 
-       if (argc < 3) {
-               fprintf(stderr, "Missing input name\n");
+       if (tarName == NULL)
+       {
+               fprintf(stderr, "The 'f' flag must be specified\n");
 
                return;
        }
 
-       devname = argv[2];
+       /*
+        * Do the correct type of action supplying the rest of the
+        * command line arguments as the list of files to process.
+        */
+       if (createFlag)
+               writeTarFile(argc, argv);
+       else
+               readTarFile(argc, argv);
+}
 
-       if (extracting + listflag != 1) {
-               fprintf(stderr, "Exactly one of 'x' or 't' must be specified\n");
 
-               return;
-       }
+/*
+ * Read a tar file and extract or list the specified files within it.
+ * If the list is empty than all files are extracted or listed.
+ */
+static void
+readTarFile(int fileCount, const char ** fileTable)
+{
+       const char *    cp;
+       int             cc;
+       int             inCc;
+       int             blockSize;
+       char            buf[BUF_SIZE];
+
+       skipFileFlag = FALSE;
+       badHeader = FALSE;
+       warnedRoot = FALSE;
+       eofFlag = FALSE;
+       inHeader = TRUE;
+       inCc = 0;
+       dataCc = 0;
+       outFd = -1;
+       blockSize = sizeof(buf);
+       cp = buf;
 
-       devfd = open(devname, 0);
+       /*
+        * Open the tar file for reading.
+        */
+       tarFd = open(tarName, O_RDONLY);
 
-       if (devfd < 0) {
-               perror(devname);
+       if (tarFd < 0)
+       {
+               perror(tarName);
 
                return;
        }
 
-       cp = buf;
-
-       while (TRUE) {
-               if ((incc == 0) && !eof) {
-                       while (incc < blocksize) {
-                               cc = read(devfd, &buf[incc], blocksize - incc);
-
-                               if (cc < 0) {
-                                       perror(devname);
-                                       goto done;
-                               }
-
-                               if (cc == 0)
-                                       break;
-
-                               incc += cc;
-                       }
-
+       /*
+        * Read blocks from the file until an end of file header block
+        * has been seen.  (A real end of file from a read is an error.)
+        */
+       while (!intFlag && !eofFlag)
+       {
+               /*
+                * Read the next block of data if necessary.
+                * This will be a large block if possible, which we will
+                * then process in the small tar blocks.
+                */
+               if (inCc <= 0)
+               {
                        cp = buf;
-               }
-
-               if (intflag) {
-                       if (extracting && (outfd >= 0))
-                               close(outfd);
+                       inCc = fullRead(tarFd, buf, blockSize);
 
-                       close(devfd);
+                       if (inCc < 0)
+                       {
+                               perror(tarName);
 
-                       return;
-               }
-
-               if (inheader) {
-                       if ((incc == 0) || eof)
                                goto done;
+                       }
+
+                       if (inCc == 0)
+                       {
+                               fprintf(stderr,
+                                       "Unexpected end of file from \"%s\"",
+                                       tarName);
 
-                       if (incc < TBLOCK) {
-                               fprintf(stderr, "Short block for header\n");
                                goto done;
                        }
+               }
 
-                       doheader((struct header *) cp);
+               /*
+                * If we are expecting a header block then examine it.
+                */
+               if (inHeader)
+               {
+                       readHeader((const TarHeader *) cp, fileCount, fileTable);
 
-                       cp += TBLOCK;
-                       incc -= TBLOCK;
+                       cp += TAR_BLOCK_SIZE;
+                       inCc -= TAR_BLOCK_SIZE;
 
                        continue;
                }
 
-               cc = incc;
+               /*
+                * We are currently handling the data for a file.
+                * Process the minimum of the amount of data we have available
+                * and the amount left to be processed for the file.
+                */
+               cc = inCc;
 
-               if (cc > datacc)
-                       cc = datacc;
+               if (cc > dataCc)
+                       cc = dataCc;
 
-               dodata(cp, cc);
+               readData(cp, cc);
 
-               if (cc % TBLOCK)
-                       cc += TBLOCK - (cc % TBLOCK);
+               /*
+                * If the amount left isn't an exact multiple of the tar block
+                * size then round it up to the next block boundary since there
+                * is padding at the end of the file.
+                */
+               if (cc % TAR_BLOCK_SIZE)
+                       cc += TAR_BLOCK_SIZE - (cc % TAR_BLOCK_SIZE);
 
                cp += cc;
-               incc -= cc;
+               inCc -= cc;
        }
 
+       /*
+        * Check for an interrupt.
+        */
+       if (intFlag)
+               fprintf(stderr, "Interrupted - aborting\n");
+
+
 done:
-       close(devfd);
+       /*
+        * Close the tar file if needed.
+        */
+       if ((tarFd >= 0) && (close(tarFd) < 0))
+               perror(tarName);
+
+       /*
+        * Close the output file if needed.
+        * This is only done here on a previous error and so no
+        * message is required on errors.
+        */
+       if (outFd >= 0)
+               (void) close(outFd);
 }
 
 
+/*
+ * Examine the header block that was just read.
+ * This can specify the information for another file, or it can mark
+ * the end of the tar file.
+ */
 static void
-doheader(hp)
-       const struct header *   hp;
+readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
 {
        int             mode;
        int             uid;
        int             gid;
-       int             chksum;
+       int             checkSum;
        long            size;
        time_t          mtime;
        const char *    name;
        int             cc;
-       BOOL            hardlink;
-       BOOL            softlink;
+       BOOL            hardLink;
+       BOOL            softLink;
 
        /*
         * If the block is completely empty, then this is the end of the
@@ -228,67 +372,117 @@ doheader(hp)
         */
        name = hp->name;
 
-       if (*name == '\0') {
-               for (cc = TBLOCK; cc > 0; cc--) {
+       if (*name == '\0')
+       {
+               for (cc = TAR_BLOCK_SIZE; cc > 0; cc--)
+               {
                        if (*name++)
                                return;
                }
 
-               eof = TRUE;
+               eofFlag = TRUE;
 
                return;
        }
 
-       mode = getoctal(hp->mode, sizeof(hp->mode));
-       uid = getoctal(hp->uid, sizeof(hp->uid));
-       gid = getoctal(hp->gid, sizeof(hp->gid));
-       size = getoctal(hp->size, sizeof(hp->size));
-       mtime = getoctal(hp->mtime, sizeof(hp->mtime));
-       chksum = getoctal(hp->chksum, sizeof(hp->chksum));
-
-       if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0)) {
-               if (!badheader)
+       /*
+        * There is another file in the archive to examine.
+        * Extract the encoded information and check it.
+        */
+       mode = getOctal(hp->mode, sizeof(hp->mode));
+       uid = getOctal(hp->uid, sizeof(hp->uid));
+       gid = getOctal(hp->gid, sizeof(hp->gid));
+       size = getOctal(hp->size, sizeof(hp->size));
+       mtime = getOctal(hp->mtime, sizeof(hp->mtime));
+       checkSum = getOctal(hp->checkSum, sizeof(hp->checkSum));
+
+       if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0))
+       {
+               if (!badHeader)
                        fprintf(stderr, "Bad tar header, skipping\n");
-               badheader = TRUE;
+
+               badHeader = TRUE;
 
                return;
        }
 
-       badheader = FALSE;
-       badwrite = FALSE;
+       badHeader = FALSE;
+       skipFileFlag = FALSE;
+
+       /*
+        * Check for the file modes.
+        */
+       hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) ||
+               (hp->typeFlag == TAR_TYPE_HARD_LINK - '0'));
 
-       hardlink = ((hp->linkflag == 1) || (hp->linkflag == '1'));
-       softlink = ((hp->linkflag == 2) || (hp->linkflag == '2'));
+       softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) ||
+               (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0'));
 
+       /*
+        * Check for a directory or a regular file.
+        */
        if (name[strlen(name) - 1] == '/')
                mode |= S_IFDIR;
        else if ((mode & S_IFMT) == 0)
                mode |= S_IFREG;
 
-       if (*name == '/') {
+       /*
+        * Check for absolute paths in the file.
+        * If we find any, then warn the user and make them relative.
+        */
+       if (*name == '/')
+       {
                while (*name == '/')
                        name++;
 
-               if (!warnedroot)
-                       fprintf(stderr, "Absolute paths detected, removing leading slashes\n");
+               if (!warnedRoot)
+               {
+                       fprintf(stderr,
+                       "Absolute path detected, removing leading slashes\n");
+               }
 
-               warnedroot = TRUE;
+               warnedRoot = TRUE;
        }
 
-       if (!extracting) {
-               if (verbose)
-                       printf("%s %3d/%-d %9ld %s %s", modestring(mode),
-                               uid, gid, size, timestring(mtime), name);
+       /*
+        * See if we want this file to be restored.
+        * If not, then set up to skip it.
+        */
+       if (!wantFileName(name, fileCount, fileTable))
+       {
+               if (!hardLink && !softLink && S_ISREG(mode))
+               {
+                       inHeader = (size == 0);
+                       dataCc = size;
+               }
+
+               skipFileFlag = TRUE;
+
+               return;
+       }
+
+       /*
+        * This file is to be handled.
+        * If we aren't extracting then just list information about the file.
+        */
+       if (!extractFlag)
+       {
+               if (verboseFlag)
+               {
+                       printf("%s %3d/%-d %9ld %s %s", modeString(mode),
+                               uid, gid, size, timeString(mtime), name);
+               }
                else
                        printf("%s", name);
 
-               if (hardlink)
-                       printf(" (link to \"%s\")", hp->linkname);
-               else if (softlink)
-                       printf(" (symlink to \"%s\")", hp->linkname);
-               else if (S_ISREG(mode)) {
-                       inheader = (size == 0);
-                       datacc = size;
+               if (hardLink)
+                       printf(" (link to \"%s\")", hp->linkName);
+               else if (softLink)
+                       printf(" (symlink to \"%s\")", hp->linkName);
+               else if (S_ISREG(mode))
+               {
+                       inHeader = (size == 0);
+                       dataCc = size;
                }
 
                printf("\n");
@@ -296,20 +490,24 @@ doheader(hp)
                return;
        }
 
-       if (verbose)
+       /*
+        * We really want to extract the file.
+        */
+       if (verboseFlag)
                printf("x %s\n", name);
 
-
-       if (hardlink) {
-               if (link(hp->linkname, name) < 0)
+       if (hardLink)
+       {
+               if (link(hp->linkName, name) < 0)
                        perror(name);
 
                return;
        }
 
-       if (softlink) {
+       if (softLink)
+       {
 #ifdef S_ISLNK
-               if (symlink(hp->linkname, name) < 0)
+               if (symlink(hp->linkName, name) < 0)
                        perror(name);
 #else
                fprintf(stderr, "Cannot create symbolic links\n");
@@ -317,64 +515,548 @@ doheader(hp)
                return;
        }
 
-       if (S_ISDIR(mode)) {
-               createpath(name, mode);
+       /*
+        * If the file is a directory, then just create the path.
+        */
+       if (S_ISDIR(mode))
+       {
+               createPath(name, mode);
 
                return;
        }
 
-       createpath(name, 0777);
+       /*
+        * There is a file to write.
+        * First create the path to it if necessary with a default permission.
+        */
+       createPath(name, 0777);
 
-       inheader = (size == 0);
-       datacc = size;
+       inHeader = (size == 0);
+       dataCc = size;
 
-       outfd = creat(name, mode);
+       /*
+        * Start the output file.
+        */
+       outFd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode);
 
-       if (outfd < 0) {
+       if (outFd < 0)
+       {
                perror(name);
-               badwrite = TRUE;
+               skipFileFlag = TRUE;
 
                return;
        }
 
-       if (size == 0) {
-               close(outfd);
-               outfd = -1;
+       /*
+        * If the file is empty, then that's all we need to do.
+        */
+       if (size == 0)
+       {
+               (void) close(outFd);
+               outFd = -1;
        }
 }
 
 
+/*
+ * Handle a data block of some specified size that was read.
+ */
+static void
+readData(const char * cp, int count)
+{
+       /*
+        * Reduce the amount of data left in this file.
+        * If there is no more data left, then we need to read
+        * the header again.
+        */
+       dataCc -= count;
+
+       if (dataCc <= 0)
+               inHeader = TRUE;
+
+       /*
+        * If we aren't extracting files or this file is being
+        * skipped then do nothing more.
+        */
+       if (!extractFlag || skipFileFlag)
+               return;
+
+       /*
+        * Write the data to the output file.
+        */
+       if (fullWrite(outFd, cp, count) < 0)
+       {
+               perror(outName);
+               (void) close(outFd);
+               outFd = -1;
+               skipFileFlag = TRUE;
+
+               return;
+       }
+
+       /*
+        * If the write failed, close the file and disable further
+        * writes to this file.
+        */
+       if (dataCc <= 0)
+       {
+               if (close(outFd))
+                       perror(outName);
+
+               outFd = -1;
+       }
+}
+
 
 /*
- * Handle a data block of some specified size.
+ * Write a tar file containing the specified files.
  */
 static void
-dodata(cp, count)
-       const char *    cp;
-       int             count;
+writeTarFile(int fileCount, const char ** fileTable)
 {
-       datacc -= count;
+       struct  stat    statbuf;
+
+       errorFlag = FALSE;
 
-       if (datacc <= 0)
-               inheader = TRUE;
+       /*
+        * Make sure there is at least one file specified.
+        */
+       if (fileCount <= 0)
+       {
+               fprintf(stderr, "No files specified to be saved\n");
 
-       if (badwrite || !extracting)
                return;
+       }
 
-       if (fullWrite(outfd, cp, count) < 0) {
-               perror(outname);
-               close(outfd);
-               outfd = -1;
-               badwrite = TRUE;
+       /*
+        * Create the tar file for writing.
+        */
+       tarFd = open(tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+       if (tarFd < 0)
+       {
+               perror(tarName);
 
                return;
        }
 
-       if (datacc <= 0) {
-               if (close(outfd))
-                       perror(outname);
+       /*
+        * Get the device and inode of the tar file for checking later.
+        */
+       if (fstat(tarFd, &statbuf) < 0)
+       {
+               perror(tarName);
+
+               goto done;
+       }
 
-               outfd = -1;
+       tarDev = statbuf.st_dev;
+       tarInode = statbuf.st_ino;
+
+       /*
+        * Append each file name into the archive file.
+        * Follow symbolic links for these top level file names.
+        */
+       while (!intFlag && !errorFlag && (fileCount-- > 0))
+       {
+               saveFile(*fileTable++, FALSE);
+       }
+
+       if (intFlag)
+               fprintf(stderr, "Interrupted - aborting archiving\n");
+
+       /*
+        * Now write an empty block of zeroes to end the archive.
+        */
+       writeTarBlock("", 1);
+
+
+done:
+       /*
+        * Close the tar file and check for errors if it was opened.
+        */
+       if ((tarFd >= 0) && (close(tarFd) < 0))
+               perror(tarName);
+}
+
+
+/*
+ * Save one file into the tar file.
+ * If the file is a directory, then this will recursively save all of
+ * the files and directories within the directory.  The seeLinks
+ * flag indicates whether or not we want to see symbolic links as
+ * they really are, instead of blindly following them.
+ */
+static void
+saveFile(const char * fileName, BOOL seeLinks)
+{
+       int             status;
+       int             mode;
+       struct stat     statbuf;
+
+       if (verboseFlag)
+               printf("a %s\n", fileName);
+
+       /*
+        * Check that the file name will fit in the header.
+        */
+       if (strlen(fileName) >= TAR_NAME_SIZE)
+       {
+               fprintf(stderr, "%s: File name is too long\n", fileName);
+
+               return;
+       }
+
+       /*
+        * Find out about the file.
+        */
+#ifdef S_ISLNK
+       if (seeLinks)
+               status = lstat(fileName, &statbuf);
+       else
+#endif
+               status = stat(fileName, &statbuf);
+
+       if (status < 0)
+       {
+               perror(fileName);
+
+               return;
+       }
+
+       /*
+        * Make sure we aren't trying to save our file into itself.
+        */
+       if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode))
+       {
+               fprintf(stderr, "Skipping saving of archive file itself\n");
+
+               return;
+       }
+
+       /*
+        * Check the type of file.
+        */
+       mode = statbuf.st_mode;
+
+       if (S_ISDIR(mode))
+       {
+               saveDirectory(fileName, &statbuf);
+
+               return;
+       }
+
+       if (S_ISREG(mode))
+       {
+               saveRegularFile(fileName, &statbuf);
+
+               return;
+       }
+
+       /*
+        * The file is a strange type of file, ignore it.
+        */
+       fprintf(stderr, "%s: not a directory or regular file\n", fileName);
+}
+
+
+/*
+ * Save a regular file to the tar file.
+ */
+static void
+saveRegularFile(const char * fileName, const struct stat * statbuf)
+{
+       BOOL            sawEof;
+       int             fileFd;
+       int             cc;
+       int             dataCount;
+       long            fullDataCount;
+       char            data[TAR_BLOCK_SIZE * 16];
+
+       /*
+        * Open the file for reading.
+        */
+       fileFd = open(fileName, O_RDONLY);
+
+       if (fileFd < 0)
+       {
+               perror(fileName);
+
+               return;
+       }
+
+       /*
+        * Write out the header for the file.
+        */
+       writeHeader(fileName, statbuf);
+
+       /*
+        * Write the data blocks of the file.
+        * We must be careful to write the amount of data that the stat
+        * buffer indicated, even if the file has changed size.  Otherwise
+        * the tar file will be incorrect.
+        */
+       fullDataCount = statbuf->st_size;
+       sawEof = FALSE;
+
+       while (!intFlag && (fullDataCount > 0))
+       {
+               /*
+                * Get the amount to write this iteration which is
+                * the minumum of the amount left to write and the
+                * buffer size.
+                */
+               dataCount = sizeof(data);
+
+               if (dataCount > fullDataCount)
+                       dataCount = (int) fullDataCount;
+
+               /*
+                * Read the data from the file if we haven't seen the
+                * end of file yet.
+                */
+               cc = 0;
+
+               if (!sawEof)
+               {
+                       cc = fullRead(fileFd, data, dataCount);
+
+                       if (cc < 0)
+                       {
+                               perror(fileName);
+
+                               (void) close(fileFd);
+                               errorFlag = TRUE;
+
+                               return;
+                       }
+
+                       /*
+                        * If the file ended too soon, complain and set
+                        * a flag so we will zero fill the rest of it.
+                        */
+                       if (cc < dataCount)
+                       {
+                               fprintf(stderr,
+                                       "%s: Short read - zero filling",
+                                       fileName);
+
+                               sawEof = TRUE;
+                       }
+               }
+
+               /*
+                * Zero fill the rest of the data if necessary.
+                */
+               if (cc < dataCount)
+                       memset(data + cc, 0, dataCount - cc);
+
+               /*
+                * Write the buffer to the TAR file.
+                */
+               writeTarBlock(data, dataCount);
+
+               fullDataCount -= dataCount;
+       }
+
+       /*
+        * Close the file.
+        */
+       if (close(fileFd) < 0)
+               fprintf(stderr, "%s: close: %s\n", fileName, strerror(errno));
+}
+
+
+/*
+ * Save a directory and all of its files to the tar file.
+ */
+static void
+saveDirectory(const char * dirName, const struct stat * statbuf)
+{
+       DIR *           dir;
+       struct dirent * entry;
+       BOOL            needSlash;
+       char            fullName[PATH_LEN];
+
+       /*
+        * Construct the directory name as used in the tar file by appending
+        * a slash character to it.
+        */
+       strcpy(fullName, dirName);
+       strcat(fullName, "/");
+
+       /*
+        * Write out the header for the directory entry.
+        */
+       writeHeader(fullName, statbuf);
+
+       /*
+        * Open the directory.
+        */
+       dir = opendir(dirName);
+
+       if (dir == NULL)
+       {
+               fprintf(stderr, "Cannot read directory \"%s\": %s\n",
+                       dirName, strerror(errno));
+
+               return;
+       }
+
+       /*
+        * See if a slash is needed.
+        */
+       needSlash = (*dirName && (dirName[strlen(dirName) - 1] != '/'));
+
+       /*
+        * Read all of the directory entries and check them,
+        * except for the current and parent directory entries.
+        */
+       while (!intFlag && !errorFlag && ((entry = readdir(dir)) != NULL))
+       {
+               if ((strcmp(entry->d_name, ".") == 0) ||
+                       (strcmp(entry->d_name, "..") == 0))
+               {
+                       continue;
+               }
+
+               /*
+                * Build the full path name to the file.
+                */
+               strcpy(fullName, dirName);
+
+               if (needSlash)
+                       strcat(fullName, "/");
+
+               strcat(fullName, entry->d_name);
+
+               /*
+                * Write this file to the tar file, noticing whether or not
+                * the file is a symbolic link.
+                */
+               saveFile(fullName, TRUE);
+       }
+
+       /*
+        * All done, close the directory.
+        */
+       closedir(dir);
+}
+
+
+/*
+ * Write a tar header for the specified file name and status.
+ * It is assumed that the file name fits.
+ */
+static void
+writeHeader(const char * fileName, const struct stat * statbuf)
+{
+       long                    checkSum;
+       const unsigned char *   cp;
+       int                     len;
+       TarHeader               header;
+
+       /*
+        * Zero the header block in preparation for filling it in.
+        */
+       memset((char *) &header, 0, sizeof(header));
+
+       /*
+        * Fill in the header.
+        */
+       strcpy(header.name, fileName);
+
+       strncpy(header.magic, TAR_MAGIC, sizeof(header.magic));
+       strncpy(header.version, TAR_VERSION, sizeof(header.version));
+
+       putOctal(header.mode, sizeof(header.mode), statbuf->st_mode & 0777);
+       putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
+       putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
+       putOctal(header.size, sizeof(header.size), statbuf->st_size);
+       putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
+
+       header.typeFlag = TAR_TYPE_REGULAR;
+
+       /*
+        * Calculate and store the checksum.
+        * This is the sum of all of the bytes of the header,
+        * with the checksum field itself treated as blanks.
+        */
+       memset(header.checkSum, ' ', sizeof(header.checkSum));
+
+       cp = (const unsigned char *) &header;
+       len = sizeof(header);
+       checkSum = 0;
+
+       while (len-- > 0)
+               checkSum += *cp++;
+
+       putOctal(header.checkSum, sizeof(header.checkSum), checkSum);
+
+       /*
+        * Write the tar header.
+        */
+       writeTarBlock((const char *) &header, sizeof(header));
+}
+
+
+/*
+ * Write data to one or more blocks of the tar file.
+ * The data is always padded out to a multiple of TAR_BLOCK_SIZE.
+ * The errorFlag static variable is set on an error.
+ */
+static void
+writeTarBlock(const char * buf, int len)
+{
+       int     partialLength;
+       int     completeLength;
+       char    fullBlock[TAR_BLOCK_SIZE];
+
+       /*
+        * If we had a write error before, then do nothing more.
+        */
+       if (errorFlag)
+               return;
+
+       /*
+        * Get the amount of complete and partial blocks.
+        */
+       partialLength = len % TAR_BLOCK_SIZE;
+       completeLength = len - partialLength;
+
+       /*
+        * Write all of the complete blocks.
+        */
+       if ((completeLength > 0) && !fullWrite(tarFd, buf, completeLength))
+       {
+               perror(tarName);
+
+               errorFlag = TRUE;
+
+               return;
+       }
+
+       /*
+        * If there are no partial blocks left, we are done.
+        */
+       if (partialLength == 0)
+               return;
+
+       /*
+        * Copy the partial data into a complete block, and pad the rest
+        * of it with zeroes.
+        */
+       memcpy(fullBlock, buf + completeLength, partialLength);
+       memset(fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength);
+
+       /*
+        * Write the last complete block.
+        */
+       if (!fullWrite(tarFd, fullBlock, TAR_BLOCK_SIZE))
+       {
+               perror(tarName);
+
+               errorFlag = TRUE;
        }
 }
 
@@ -386,28 +1068,27 @@ dodata(cp, count)
  * here, as failures to restore files can be reported later.
  */
 static void
-createpath(name, mode)
-       const char *    name;
-       int             mode;
+createPath(const char * name, int mode)
 {
        char *  cp;
-       char *  cpold;
-       char    buf[NAMSIZ];
+       char *  cpOld;
+       char    buf[TAR_NAME_SIZE];
 
        strcpy(buf, name);
 
        cp = strchr(buf, '/');
 
-       while (cp) {
-               cpold = cp;
+       while (cp)
+       {
+               cpOld = cp;
                cp = strchr(cp + 1, '/');
 
-               *cpold = '\0';
+               *cpOld = '\0';
 
                if (mkdir(buf, cp ? 0777 : mode) == 0)
                        printf("Directory \"%s\" created\n", buf);
 
-               *cpold = '/';
+               *cpOld = '/';
        }
 }
 
@@ -418,28 +1099,29 @@ createpath(name, mode)
  * at the end.  Returns -1 on an illegal format.
  */
 static long
-getoctal(cp, len)
-       const char *    cp;
-       int             len;
+getOctal(const char * cp, int len)
 {
        long    val;
 
-       while ((len > 0) && (*cp == ' ')) {
+       while ((len > 0) && (*cp == ' '))
+       {
                cp++;
                len--;
        }
 
-       if ((len == 0) || !isoctal(*cp))
+       if ((len == 0) || !isOctal(*cp))
                return -1;
 
        val = 0;
 
-       while ((len > 0) && isoctal(*cp)) {
+       while ((len > 0) && isOctal(*cp))
+       {
                val = val * 8 + *cp++ - '0';
                len--;
        }
 
-       while ((len > 0) && (*cp == ' ')) {
+       while ((len > 0) && (*cp == ' '))
+       {
                cp++;
                len--;
        }
@@ -450,4 +1132,102 @@ getoctal(cp, len)
        return val;
 }
 
+
+/*
+ * Put an octal string into the specified buffer.
+ * The number is zero and space padded and possibly null padded.
+ * Returns TRUE if successful.
+ */
+static BOOL
+putOctal(char * cp, int len, long value)
+{
+       int     tempLength;
+       char *  tempString;
+       char    tempBuffer[32];
+
+       /*
+        * Create a string of the specified length with an initial space,
+        * leading zeroes and the octal number, and a trailing null.
+        */
+       tempString = tempBuffer;
+
+       sprintf(tempString, " %0*lo", len - 2, value);
+
+       tempLength = strlen(tempString) + 1;
+
+       /*
+        * If the string is too large, suppress the leading space.
+        */
+       if (tempLength > len)
+       {
+               tempLength--;
+               tempString++;
+       }
+
+       /*
+        * If the string is still too large, suppress the trailing null.
+        */
+       if (tempLength > len)
+               tempLength--;
+
+       /*
+        * If the string is still too large, fail.
+        */
+       if (tempLength > len)
+               return FALSE;
+
+       /*
+        * Copy the string to the field.
+        */
+       memcpy(cp, tempString, len);
+
+       return TRUE;
+}
+
+
+/*
+ * See if the specified file name belongs to one of the specified list
+ * of path prefixes.  An empty list implies that all files are wanted.
+ * Returns TRUE if the file is selected.
+ */
+static BOOL
+wantFileName(const char * fileName, int fileCount, const char ** fileTable)
+{
+       const char *    pathName;
+       int             fileLength;
+       int             pathLength;
+
+       /*
+        * If there are no files in the list, then the file is wanted.
+        */
+       if (fileCount == 0)
+               return TRUE;
+
+       fileLength = strlen(fileName);
+
+       /*
+        * Check each of the test paths.
+        */
+       while (fileCount-- > 0)
+       {
+               pathName = *fileTable++;
+
+               pathLength = strlen(pathName);
+
+               if (fileLength < pathLength)
+                       continue;
+
+               if (memcmp(fileName, pathName, pathLength) != 0)
+                       continue;
+
+               if ((fileLength == pathLength) ||
+                       (fileName[pathLength] == '/'))
+               {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
 /* END CODE */
diff --git a/cmds.c b/cmds.c
index f4a5e60545689c3b13aa61cc5922a69185d3353e..6f3250d8e60973ce9229d5adc6c030e3d481de7c 100644 (file)
--- a/cmds.c
+++ b/cmds.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
 
 
 void
-do_echo(argc, argv)
-       int             argc;
-       const char **   argv;
+do_echo(int argc, const char ** argv)
 {
        BOOL    first;
 
        first = TRUE;
 
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                if (!first)
                        fputc(' ', stdout);
 
                first = FALSE;
-               fputs(*++argv, stdout);
+               fputs(*(++argv), stdout);
        }
 
        fputc('\n', stdout);
@@ -41,13 +40,12 @@ do_echo(argc, argv)
 
 
 void
-do_pwd(argc, argv)
-       int             argc;
-       const char **   argv;
+do_pwd(int argc, const char ** argv)
 {
-       char    buf[PATHLEN];
+       char    buf[PATH_LEN];
 
-       if (getcwd(buf, PATHLEN) == NULL) {
+       if (getcwd(buf, PATH_LEN) == NULL)
+       {
                fprintf(stderr, "Cannot get current directory\n");
 
                return;
@@ -58,18 +56,18 @@ do_pwd(argc, argv)
 
 
 void
-do_cd(argc, argv)
-       int             argc;
-       const char **   argv;
+do_cd(int argc, const char ** argv)
 {
        const char *    path;
 
        if (argc > 1)
                path = argv[1];
-       else {
+       else
+       {
                path = getenv("HOME");
 
-               if (path == NULL) {
+               if (path == NULL)
+               {
                        fprintf(stderr, "No HOME environment variable\n");
 
                        return;
@@ -82,11 +80,10 @@ do_cd(argc, argv)
 
 
 void
-do_mkdir(argc, argv)
-       int             argc;
-       const char **   argv;
+do_mkdir(int argc, const char ** argv)
 {
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                if (mkdir(argv[1], 0777) < 0)
                        perror(argv[1]);
 
@@ -96,9 +93,7 @@ do_mkdir(argc, argv)
 
 
 void
-do_mknod(argc, argv)
-       int             argc;
-       const char **   argv;
+do_mknod(int argc, const char ** argv)
 {
        const char *    cp;
        int             mode;
@@ -111,7 +106,8 @@ do_mknod(argc, argv)
                mode |= S_IFBLK;
        else if (strcmp(argv[2], "c") == 0)
                mode |= S_IFCHR;
-       else {
+       else
+       {
                fprintf(stderr, "Bad device type\n");
 
                return;
@@ -120,10 +116,11 @@ do_mknod(argc, argv)
        major = 0;
        cp = argv[3];
 
-       while (isdecimal(*cp))
+       while (isDecimal(*cp))
                major = major * 10 + *cp++ - '0';
 
-       if (*cp || (major < 0) || (major > 255)) {
+       if (*cp || (major < 0) || (major > 255))
+       {
                fprintf(stderr, "Bad major number\n");
 
                return;
@@ -132,10 +129,11 @@ do_mknod(argc, argv)
        minor = 0;
        cp = argv[4];
 
-       while (isdecimal(*cp))
+       while (isDecimal(*cp))
                minor = minor * 10 + *cp++ - '0';
 
-       if (*cp || (minor < 0) || (minor > 255)) {
+       if (*cp || (minor < 0) || (minor > 255))
+       {
                fprintf(stderr, "Bad minor number\n");
 
                return;
@@ -147,11 +145,10 @@ do_mknod(argc, argv)
 
 
 void
-do_rmdir(argc, argv)
-       int             argc;
-       const char **   argv;
+do_rmdir(int argc, const char ** argv)
 {
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                if (rmdir(argv[1]) < 0)
                        perror(argv[1]);
 
@@ -161,20 +158,17 @@ do_rmdir(argc, argv)
 
 
 void
-do_sync(argc, argv)
-       int             argc;
-       const char **   argv;
+do_sync(int argc, const char ** argv)
 {
        sync();
 }
 
 
 void
-do_rm(argc, argv)
-       int             argc;
-       const char **   argv;
+do_rm(int argc, const char ** argv)
 {
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                if (unlink(argv[1]) < 0)
                        perror(argv[1]);
 
@@ -184,9 +178,7 @@ do_rm(argc, argv)
 
 
 void
-do_chmod(argc, argv)
-       int             argc;
-       const char **   argv;
+do_chmod(int argc, const char ** argv)
 {
        const char *    cp;
        int             mode;
@@ -194,10 +186,11 @@ do_chmod(argc, argv)
        mode = 0;
        cp = argv[1];
 
-       while (isoctal(*cp))
+       while (isOctal(*cp))
                mode = mode * 8 + (*cp++ - '0');
 
-       if (*cp) {
+       if (*cp)
+       {
                fprintf(stderr, "Mode must be octal\n");
 
                return;
@@ -206,7 +199,8 @@ do_chmod(argc, argv)
        argc--;
        argv++;
 
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                if (chmod(argv[1], mode) < 0)
                        perror(argv[1]);
 
@@ -216,24 +210,24 @@ do_chmod(argc, argv)
 
 
 void
-do_chown(argc, argv)
-       int             argc;
-       const char **   argv;
+do_chown(int argc, const char ** argv)
 {
        const char *    cp;
        int             uid;
        struct passwd * pwd;
-       struct stat     statbuf;
+       struct stat     statBuf;
 
        cp = argv[1];
 
-       if (isdecimal(*cp)) {
+       if (isDecimal(*cp))
+       {
                uid = 0;
 
-               while (isdecimal(*cp))
+               while (isDecimal(*cp))
                        uid = uid * 10 + (*cp++ - '0');
 
-               if (*cp) {
+               if (*cp)
+               {
                        fprintf(stderr, "Bad uid value\n");
 
                        return;
@@ -241,7 +235,8 @@ do_chown(argc, argv)
        } else {
                pwd = getpwnam(cp);
 
-               if (pwd == NULL) {
+               if (pwd == NULL)
+               {
                        fprintf(stderr, "Unknown user name\n");
 
                        return;
@@ -253,11 +248,12 @@ do_chown(argc, argv)
        argc--;
        argv++;
 
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                argv++;
 
-               if ((stat(*argv, &statbuf) < 0) ||
-                       (chown(*argv, uid, statbuf.st_gid) < 0))
+               if ((stat(*argv, &statBuf) < 0) ||
+                       (chown(*argv, uid, statBuf.st_gid) < 0))
                {
                        perror(*argv);
                }
@@ -266,32 +262,35 @@ do_chown(argc, argv)
 
 
 void
-do_chgrp(argc, argv)
-       int             argc;
-       const char **   argv;
+do_chgrp(int argc, const char ** argv)
 {
        const char *    cp;
        int             gid;
        struct group *  grp;
-       struct stat     statbuf;
+       struct stat     statBuf;
 
        cp = argv[1];
 
-       if (isdecimal(*cp)) {
+       if (isDecimal(*cp))
+       {
                gid = 0;
 
-               while (isdecimal(*cp))
+               while (isDecimal(*cp))
                        gid = gid * 10 + (*cp++ - '0');
 
-               if (*cp) {
+               if (*cp)
+               {
                        fprintf(stderr, "Bad gid value\n");
 
                        return;
                }
-       } else {
+       }
+       else
+       {
                grp = getgrnam(cp);
 
-               if (grp == NULL) {
+               if (grp == NULL)
+               {
                        fprintf(stderr, "Unknown group name\n");
 
                        return;
@@ -303,11 +302,12 @@ do_chgrp(argc, argv)
        argc--;
        argv++;
 
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                argv++;
 
-               if ((stat(*argv, &statbuf) < 0) ||
-                       (chown(*argv, statbuf.st_uid, gid) < 0))
+               if ((stat(*argv, &statBuf) < 0) ||
+                       (chown(*argv, statBuf.st_uid, gid) < 0))
                {
                        perror(*argv);
                }
@@ -316,9 +316,7 @@ do_chgrp(argc, argv)
 
 
 void
-do_touch(argc, argv)
-       int             argc;
-       const char **   argv;
+do_touch(int argc, const char ** argv)
 {
        const char *    name;
        int             fd;
@@ -327,12 +325,14 @@ do_touch(argc, argv)
        time(&now.actime);
        now.modtime = now.actime;
 
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                name = *(++argv);
 
                fd = open(name, O_CREAT | O_WRONLY | O_EXCL, 0666);
 
-               if (fd >= 0) {
+               if (fd >= 0)
+               {
                        close(fd);
 
                        continue;
@@ -345,75 +345,78 @@ do_touch(argc, argv)
 
 
 void
-do_mv(argc, argv)
-       int             argc;
-       const char **   argv;
+do_mv(int argc, const char ** argv)
 {
-       const char *    srcname;
-       const char *    destname;
-       const char *    lastarg;
-       BOOL            dirflag;
+       const char *    srcName;
+       const char *    destName;
+       const char *    lastArg;
+       BOOL            dirFlag;
 
-       lastarg = argv[argc - 1];
+       lastArg = argv[argc - 1];
 
-       dirflag = isadir(lastarg);
+       dirFlag = isDirectory(lastArg);
 
-       if ((argc > 3) && !dirflag) {
-               fprintf(stderr, "%s: not a directory\n", lastarg);
+       if ((argc > 3) && !dirFlag)
+       {
+               fprintf(stderr, "%s: not a directory\n", lastArg);
 
                return;
        }
 
-       while (!intflag && (argc-- > 2)) {
-               srcname = *(++argv);
+       while (!intFlag && (argc-- > 2))
+       {
+               srcName = *(++argv);
 
-               if (access(srcname, 0) < 0) {
-                       perror(srcname);
+               if (access(srcName, 0) < 0)
+               {
+                       perror(srcName);
 
                        continue;
                }
 
-               destname = lastarg;
+               destName = lastArg;
 
-               if (dirflag)
-                       destname = buildname(destname, srcname);
+               if (dirFlag)
+                       destName = buildName(destName, srcName);
 
-               if (rename(srcname, destname) >= 0)
+               if (rename(srcName, destName) >= 0)
                        continue;
 
-               if (errno != EXDEV) {
-                       perror(destname);
+               if (errno != EXDEV)
+               {
+                       perror(destName);
 
                        continue;
                }
 
-               if (!copyfile(srcname, destname, TRUE))
+               if (!copyFile(srcName, destName, TRUE))
                        continue;
 
-               if (unlink(srcname) < 0)
-                       perror(srcname);
+               if (unlink(srcName) < 0)
+                       perror(srcName);
        }
 }
 
 
 void
-do_ln(argc, argv)
-       int             argc;
-       const char **   argv;
+do_ln(int argc, const char ** argv)
 {
-       const char *    srcname;
-       const char *    destname;
-       const char *    lastarg;
-       BOOL            dirflag;
+       const char *    srcName;
+       const char *    destName;
+       const char *    lastArg;
+       BOOL            dirFlag;
 
-       if (argv[1][0] == '-') {
-               if (strcmp(argv[1], "-s")) {
+       if (argv[1][0] == '-')
+       {
+               if (strcmp(argv[1], "-s"))
+               {
                        fprintf(stderr, "Unknown option\n");
 
                        return;
                }
 
-               if (argc != 4) {
+               if (argc != 4)
+               {
                        fprintf(stderr, "Wrong number of arguments for symbolic link\n");
 
                        return;
@@ -431,31 +434,35 @@ do_ln(argc, argv)
        /*
         * Here for normal hard links.
         */
-       lastarg = argv[argc - 1];
-       dirflag = isadir(lastarg);
+       lastArg = argv[argc - 1];
+       dirFlag = isDirectory(lastArg);
 
-       if ((argc > 3) && !dirflag) {
-               fprintf(stderr, "%s: not a directory\n", lastarg);
+       if ((argc > 3) && !dirFlag)
+       {
+               fprintf(stderr, "%s: not a directory\n", lastArg);
 
                return;
        }
 
-       while (argc-- > 2) {
-               srcname = *(++argv);
+       while (argc-- > 2)
+       {
+               srcName = *(++argv);
 
-               if (access(srcname, 0) < 0) {
-                       perror(srcname);
+               if (access(srcName, 0) < 0)
+               {
+                       perror(srcName);
 
                        continue;
                }
 
-               destname = lastarg;
+               destName = lastArg;
 
-               if (dirflag)
-                       destname = buildname(destname, srcname);
+               if (dirFlag)
+                       destName = buildName(destName, srcName);
 
-               if (link(srcname, destname) < 0) {
-                       perror(destname);
+               if (link(srcName, destName) < 0)
+               {
+                       perror(destName);
 
                        continue;
                }
@@ -464,41 +471,39 @@ do_ln(argc, argv)
 
 
 void
-do_cp(argc, argv)
-       int             argc;
-       const char **   argv;
+do_cp(int argc, const char ** argv)
 {
-       const char *    srcname;
-       const char *    destname;
-       const char *    lastarg;
-       BOOL            dirflag;
+       const char *    srcName;
+       const char *    destName;
+       const char *    lastArg;
+       BOOL            dirFlag;
 
-       lastarg = argv[argc - 1];
+       lastArg = argv[argc - 1];
 
-       dirflag = isadir(lastarg);
+       dirFlag = isDirectory(lastArg);
 
-       if ((argc > 3) && !dirflag) {
-               fprintf(stderr, "%s: not a directory\n", lastarg);
+       if ((argc > 3) && !dirFlag)
+       {
+               fprintf(stderr, "%s: not a directory\n", lastArg);
 
                return;
        }
 
-       while (!intflag && (argc-- > 2)) {
-               srcname = *argv++;
-               destname = lastarg;
+       while (!intFlag && (argc-- > 2))
+       {
+               srcName = *(++argv);
+               destName = lastArg;
 
-               if (dirflag)
-                       destname = buildname(destname, srcname);
+               if (dirFlag)
+                       destName = buildName(destName, srcName);
 
-               (void) copyfile(srcname, destname, FALSE);
+               (void) copyFile(srcName, destName, FALSE);
        }
 }
 
 
 void
-do_mount(argc, argv)
-       int             argc;
-       const char **   argv;
+do_mount(int argc, const char ** argv)
 {
        const char *    str;
        const char *    type;
@@ -509,13 +514,16 @@ do_mount(argc, argv)
        type = "ext2";
        flags = MS_MGC_VAL;
 
-       while ((argc > 0) && (**argv == '-')) {
+       while ((argc > 0) && (**argv == '-'))
+       {
                argc--;
-               str = *argv++ ;
+               str = *argv++;
 
-               while (*++str) switch (*str) {
+               while (*++str) switch (*str)
+               {
                        case 't':
-                               if ((argc <= 0) || (**argv == '-')) {
+                               if ((argc <= 0) || (**argv == '-'))
+                               {
                                        fprintf(stderr, "Missing file system type\n");
 
                                        return;
@@ -540,7 +548,8 @@ do_mount(argc, argv)
                }
        }
 
-       if (argc != 2) {
+       if (argc != 2)
+       {
                fprintf(stderr, "Wrong number of arguments for mount\n");
 
                return;
@@ -552,9 +561,7 @@ do_mount(argc, argv)
 
 
 void
-do_umount(argc, argv)
-       int             argc;
-       const char **   argv;
+do_umount(int argc, const char ** argv)
 {
        if (umount(argv[1]) < 0)
                perror(argv[1]);
@@ -562,9 +569,7 @@ do_umount(argc, argv)
 
 
 void
-do_cmp(argc, argv)
-       int             argc;
-       const char **   argv;
+do_cmp(int argc, const char ** argv)
 {
        int             fd1;
        int             fd2;
@@ -573,32 +578,35 @@ do_cmp(argc, argv)
        long            pos;
        const char *    bp1;
        const char *    bp2;
-       char            buf1[BUFSIZE];
-       char            buf2[BUFSIZE];
-       struct  stat    statbuf1;
-       struct  stat    statbuf2;
+       char            buf1[BUF_SIZE];
+       char            buf2[BUF_SIZE];
+       struct  stat    statBuf1;
+       struct  stat    statBuf2;
 
-       if (stat(argv[1], &statbuf1) < 0) {
+       if (stat(argv[1], &statBuf1) < 0)
+       {
                perror(argv[1]);
 
                return;
        }
 
-       if (stat(argv[2], &statbuf2) < 0) {
+       if (stat(argv[2], &statBuf2) < 0)
+       {
                perror(argv[2]);
 
                return;
        }
 
-       if ((statbuf1.st_dev == statbuf2.st_dev) &&
-               (statbuf1.st_ino == statbuf2.st_ino))
+       if ((statBuf1.st_dev == statBuf2.st_dev) &&
+               (statBuf1.st_ino == statBuf2.st_ino))
        {
                printf("Files are links to each other\n");
 
                return;
        }
 
-       if (statbuf1.st_size != statbuf2.st_size) {
+       if (statBuf1.st_size != statBuf2.st_size)
+       {
                printf("Files are different sizes\n");
 
                return;
@@ -606,7 +614,8 @@ do_cmp(argc, argv)
 
        fd1 = open(argv[1], O_RDONLY);
 
-       if (fd1 < 0) {
+       if (fd1 < 0)
+       {
                perror(argv[1]);
 
                return;
@@ -614,7 +623,8 @@ do_cmp(argc, argv)
 
        fd2 = open(argv[2], O_RDONLY);
 
-       if (fd2 < 0) {
+       if (fd2 < 0)
+       {
                perror(argv[2]);
                close(fd1);
 
@@ -623,40 +633,47 @@ do_cmp(argc, argv)
 
        pos = 0;
 
-       while (TRUE) {
-               if (intflag)
+       while (TRUE)
+       {
+               if (intFlag)
                        goto closefiles;
 
                cc1 = read(fd1, buf1, sizeof(buf1));
 
-               if (cc1 < 0) {
+               if (cc1 < 0)
+               {
                        perror(argv[1]);
                        goto closefiles;
                }
 
                cc2 = read(fd2, buf2, sizeof(buf2));
 
-               if (cc2 < 0) {
+               if (cc2 < 0)
+               {
                        perror(argv[2]);
                        goto closefiles;
                }
 
-               if ((cc1 == 0) && (cc2 == 0)) {
+               if ((cc1 == 0) && (cc2 == 0))
+               {
                        printf("Files are identical\n");
                        goto closefiles;
                }
 
-               if (cc1 < cc2) {
+               if (cc1 < cc2)
+               {
                        printf("First file is shorter than second\n");
                        goto closefiles;
                }
 
-               if (cc1 > cc2) {
+               if (cc1 > cc2)
+               {
                        printf("Second file is shorter than first\n");
                        goto closefiles;
                }
 
-               if (memcmp(buf1, buf2, cc1) == 0) {
+               if (memcmp(buf1, buf2, cc1) == 0)
+               {
                        pos += cc1;
 
                        continue;
@@ -664,6 +681,7 @@ do_cmp(argc, argv)
 
                bp1 = buf1;
                bp2 = buf2;
+
                while (*bp1++ == *bp2++)
                        pos++;
 
@@ -679,9 +697,7 @@ closefiles:
 
 
 void
-do_more(argc, argv)
-       int             argc;
-       const char **   argv;
+do_more(int argc, const char ** argv)
 {
        FILE *          fp;
        const char *    name;
@@ -718,12 +734,14 @@ do_more(argc, argv)
        /*
         * OK, process each file.
         */
-       while (argc-- > 1) {
+       while (argc-- > 1)
+       {
                name = *(++argv);
 
                fp = fopen(name, "r");
 
-               if (fp == NULL) {
+               if (fp == NULL)
+               {
                        perror(name);
 
                        return;
@@ -733,8 +751,10 @@ do_more(argc, argv)
                line = 1;
                col = 0;
 
-               while (fp && ((ch = fgetc(fp)) != EOF)) {
-                       switch (ch) {
+               while (fp && ((ch = fgetc(fp)) != EOF))
+               {
+                       switch (ch)
+                       {
                                case '\r':
                                        col = 0;
                                        break;
@@ -759,7 +779,8 @@ do_more(argc, argv)
 
                        putchar(ch);
 
-                       if (col >= pageColumns) {
+                       if (col >= pageColumns)
+                       {
                                col -= pageColumns;
                                line++;
                        }
@@ -773,7 +794,8 @@ do_more(argc, argv)
                        printf("--More--");
                        fflush(stdout);
 
-                       if (intflag || (read(0, buf, sizeof(buf)) < 0)) {
+                       if (intFlag || (read(0, buf, sizeof(buf)) < 0))
+                       {
                                if (fp)
                                        fclose(fp);
 
@@ -785,7 +807,8 @@ do_more(argc, argv)
                        if (ch == ':')
                                ch = buf[1];
 
-                       switch (ch) {
+                       switch (ch)
+                       {
                                case 'N':
                                case 'n':
                                        fclose(fp);
@@ -810,18 +833,81 @@ do_more(argc, argv)
 
 
 void
-do_exit(argc, argv)
-       int             argc;
-       const char **   argv;
+do_sum(int argc, const char ** argv)
 {
+       const char *    name;
+       int             fd;
+       int             cc;
+       int             ch;
+       int             i;
+       unsigned long   checksum;
+       char            buf[BUF_SIZE];
+
+       argc--;
+       argv++;
+
+       while (argc-- > 0)
+       {
+               name = *argv++;
+
+               fd = open(name, O_RDONLY);
+
+               if (fd < 0)
+               {
+                       perror(name);
+
+                       continue;
+               }
+
+               checksum = 0;
+
+               while ((cc = read(fd, buf, sizeof(buf))) > 0)
+               {
+                       for (i = 0; i < cc; i++)
+                       {
+                               ch = buf[i];
+
+                               if ((checksum & 0x01) != 0)
+                                       checksum = (checksum >> 1) + 0x8000;
+                               else
+                                       checksum = (checksum >> 1);
+
+                               checksum = (checksum + ch) & 0xffff;
+                       }
+               }
+
+               if (cc < 0)
+               {
+                       perror(name);
+
+                       (void) close(fd);
+
+                       continue;
+               }
+
+               (void) close(fd);
+
+               printf("%05lu %s\n", checksum, name);
+       }
+}
+
+
+void
+do_exit(int argc, const char ** argv)
+{
+       if (getpid() == 1)
+       {
+               fprintf(stderr, "You are the INIT process!\n");
+
+               return;
+       }
+
        exit(0);
 }
 
 
 void
-do_setenv(argc, argv)
-       int             argc;
-       const char **   argv;
+do_setenv(int argc, const char ** argv)
 {
        const char *    name;
        const char *    value;
@@ -852,9 +938,7 @@ do_setenv(argc, argv)
 
 
 void
-do_printenv(argc, argv)
-       int             argc;
-       const char **   argv;
+do_printenv(int argc, const char ** argv)
 {
        const char **   env;
        extern char **  environ;
@@ -862,7 +946,8 @@ do_printenv(argc, argv)
 
        env = (const char **) environ;
 
-       if (argc == 1) {
+       if (argc == 1)
+       {
                while (*env)
                        printf("%s\n", *env++);
 
@@ -871,7 +956,8 @@ do_printenv(argc, argv)
 
        len = strlen(argv[1]);
 
-       while (*env) {
+       while (*env)
+       {
                if ((strlen(*env) > len) && (env[0][len] == '=') &&
                        (memcmp(argv[1], *env, len) == 0))
                {
@@ -885,14 +971,13 @@ do_printenv(argc, argv)
 
 
 void
-do_umask(argc, argv)
-       int             argc;
-       const char **   argv;
+do_umask(int argc, const char ** argv)
 {
        const char *    cp;
        int             mask;
 
-       if (argc <= 1) {
+       if (argc <= 1)
+       {
                mask = umask(0);
                umask(mask);
                printf("%03o\n", mask);
@@ -903,10 +988,11 @@ do_umask(argc, argv)
        mask = 0;
        cp = argv[1];
 
-       while (isoctal(*cp))
+       while (isOctal(*cp))
                mask = mask * 8 + *cp++ - '0';
 
-       if (*cp || (mask & ~0777)) {
+       if (*cp || (mask & ~0777))
+       {
                fprintf(stderr, "Bad umask value\n");
 
                return;
@@ -917,9 +1003,7 @@ do_umask(argc, argv)
 
 
 void
-do_kill(argc, argv)
-       int             argc;
-       const char **   argv;
+do_kill(int argc, const char ** argv)
 {
        const char *    cp;
        int             sig;
@@ -927,7 +1011,8 @@ do_kill(argc, argv)
 
        sig = SIGTERM;
 
-       if (argv[1][0] == '-') {
+       if (argv[1][0] == '-')
+       {
                cp = &argv[1][1];
 
                if (strcmp(cp, "HUP") == 0)
@@ -938,13 +1023,25 @@ do_kill(argc, argv)
                        sig = SIGQUIT;
                else if (strcmp(cp, "KILL") == 0)
                        sig = SIGKILL;
-               else {
+               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
+               {
                        sig = 0;
 
-                       while (isdecimal(*cp))
+                       while (isDecimal(*cp))
                                sig = sig * 10 + *cp++ - '0';
 
-                       if (*cp) {
+                       if (*cp)
+                       {
                                fprintf(stderr, "Unknown signal\n");
 
                                return;
@@ -955,14 +1052,16 @@ do_kill(argc, argv)
                argv++;
        }
 
-       while (argc-- > 1) {
-               cp = *++argv;
+       while (argc-- > 1)
+       {
+               cp = *(++argv);
                pid = 0;
 
-               while (isdecimal(*cp))
+               while (isDecimal(*cp))
                        pid = pid * 10 + *cp++ - '0';
 
-               if (*cp) {
+               if (*cp)
+               {
                        fprintf(stderr, "Non-numeric pid\n");
 
                        return;
@@ -975,9 +1074,7 @@ do_kill(argc, argv)
 
 
 void
-do_where(argc, argv)
-       int             argc;
-       const char **   argv;
+do_where(int argc, const char ** argv)
 {
        const char *    program;
        const char *    dirName;
@@ -998,7 +1095,7 @@ do_where(argc, argv)
 
        path = getenv("PATH");
 
-       fullPath = getchunk(strlen(path) + strlen(program) + 2);
+       fullPath = getChunk(strlen(path) + strlen(program) + 2);
        path = chunkstrdup(path);
 
        if ((path == NULL) || (fullPath == NULL))
diff --git a/sash.1 b/sash.1
index 68214c43f8ca628329c3f0319a7ada588e25e327..23e85879f5e88f7ec43b49fecf58ca9d03c3aedd 100644 (file)
--- a/sash.1
+++ b/sash.1
@@ -2,32 +2,31 @@
 .SH NAME
 sash \- stand-alone shell with built-in commands
 .SH SYNOPSYS
-.B sash
+.B sash [-c command] [-p prompt] [-q] [-a]
 .SH DESCRIPTION
 The
 .B sash
 program is a stand-alone shell which is useful for recovering from certain
-types of system failures.  In particular, it was created in order to cope
-with the problem of missing shared libraries.
-You can also use
-.B sash
-to safely upgrade to new versions of the shared libraries.
+types of system failures.
+In particular, it was created in order to cope with the problem of
+missing shared libraries or important executables.
 .PP
 .B Sash
 can execute external programs, as in any shell.  There are no restrictions
 on these commands, as the standard shell is used to execute them if there
-are any meta-characters in the command.
+are any non-wildcard meta-characters in the command.
 .PP
-More importantly, however, is that many of the standard system commands
-are built-in to
+More importantly, however,
+is that many of the standard system commands are built-in to
 .BR sash .
 These built-in commands are:
 .PP
 .nf
-     -chgrp, -chmod, -chown, -cmp, -cp, -dd, -echo,
-     -ed, -grep, -gunzip, -gzip, -kill, -ln, -ls, -mkdir,
-     -mknod, -more, -mount, -mv, -printenv, -pwd, -rm,
-     -rmdir, -sync, -tar, -touch, -umount, -where
+     -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
 .fi
 .PP
 These commands are generally similar to the standard programs with similar
@@ -44,11 +43,12 @@ If "-ls" is typed, then the built-in command which mimics
 .B ls
 is called.
 .PP
-For the built-in commands, filenames are expanded so that asterisks,
+For the built-in commands, file names are expanded so that asterisks,
 question marks, and characters inside of square brackets are recognised
-and are expanded.  However, no other command line processing is performed.
-This includes quoting of arguments, specifying of file redirection, and
-the specifying of a pipeline.
+and are expanded.
+Arguments can be quoted using single quotes, double quotes, or backslashes.
+However, no other command line processing is performed.
+This includes specifying of file redirection, and the specifying of a pipeline.
 .PP
 If an external program is non-existant or fails to run correctly, then
 the "alias" built-in command may be used to redefine the standard command
@@ -56,6 +56,8 @@ so that it automatically runs the built-in command instead.  For example,
 the command "alias ls -ls" redefines "ls" to run the built-in command.
 This saves you the pain of having to remember to type the leading dash
 all of the time.
+If many external programs will not run, then the "aliasall" command may
+be useful to create multiple aliases.
 .PP
 The "help" command will list all of the built-in commands in
 .B sash .
@@ -69,56 +71,86 @@ If
 .I name
 and
 .I command
-are provided, this defines an alias for a command
-with the specified name, which executes the specified command, with
-possible arguments.  If just
+are provided, this defines an alias for a command with the specified name
+which executes the specified command with possible arguments.
+Arguments containing wildcards can be quoted in order to defer their
+expansion until the alias is invoked.
+If just
 .I name
 is provided, then the definition
 of the specified command alias is displayed.  If nothing is provided,
-then the definitions of all aliases are displayed.  When defining an
-alias, wildcards are not expanded.
-.TP
-.B cd [dirname]
+then the definitions of all aliases are displayed.
+.TP
+.B aliasall
+This defines aliases for all of the built-in commands that start with
+dashes to the corresponding names without the dashes.
+This may be useful when the system is so corrupted that no external
+programs may be executed at all.
+.TP
+.B -ar [txp][v] arfile [filename]...
+List or extract files from an ar archive.
+The arfile argument specifies a file name which contains the archive.
+If no additional filenames are specified, then all files in the archive are
+operated on.
+Otherwise, only those archive members which have the same name 
+as one of the additional filenames are operated on.
+Filenames which do not appear in the archive are ignored.
+Archives cannot be created or modified.
+The archiver correctly handles 4.0BSD archives,
+and understands both the SysV and 4.4BSD extensions for long file names.
+The extended pseudo-BSD formats are not supported;
+nor are the two antediluvian binary formats derived from V7 and earlier.
+(The GNU archiver normally creates archives in the 4.0BSD format with
+SysV extensions.)
+.TP
+.B cd [dirName]
 If
-.I dirname
+.I dirName
 is provided, then the current directory is changed to the
-dirname.  If
-.I dirname
+dirName.  If
+.I dirName
 is absent, then the current directory is changed
 to the user's home directory (value of the $HOME environment variable).
 .TP
-.B -chgrp gid filename ...
+.B -chattr [+i] [-i] [+a] [-a] fileName ...
+Change the attributes of the specified files on the ext2 file system.
+Using a plus sign adds the specified attribute for the files.
+Using a minus sign removes the specified attributes for the files.
+The 'i' attribute makes a file immutable so that it cannot be changed.
+The 'a' attribute makes a file append-only.
+.TP
+.B -chgrp gid fileName ...
 Change the group id for the specified list of files.  The
 .I gid
 can
 either be a group name, or a decimal value.
 .TP
-.B -chmod mode filename ...
+.B -chmod mode fileName ...
 Change the mode of the specified list of files.  The
 .I mode
 argument
 can only be an octal value.
 .TP
-.B -chown uid filename ...
+.B -chown uid fileName ...
 Change the owner id for the specified list of files.  The
 .I uid
 can
 either be a user name, or a decimal value.
 .TP
-.B -cmp filename1 filename2
-Determines whether or not the specified filenames have identical data.
+.B -cmp fileName1 fileName2
+Determines whether or not the specified file names have identical data.
 This says that the files are links to each other, are different sizes,
 differ at a particular byte number, or are identical.
 .TP
-.B -cp srcname ... destname
+.B -cp srcName ... destName
 Copies one or more files from the
-.I srcname
+.I srcName
 to the
-.IR destname .
+.IR destName .
 If more
-than one srcname is given, or if destname is a directory, then all
-the srcnames are copied into the destname directory with the same
-names as the srcnames.
+than one srcName is given, or if destName is a directory, then all
+the srcNames are copied into the destName directory with the same
+names as the srcNames.
 .TP
 .B -dd if=name of=name [bs=n] [count=n] [skip=n] [seek=n]
 Copy data from one file to another with the specified parameters.
@@ -146,11 +178,11 @@ respectively.  The command reports the number of full blocks read
 and written, and whether or not any partial block was read or written.
 .TP
 .B -echo [args] ...
-Echo the arguments to the -echo command.  Wildcards are expanded, so
-this is convenient to get a quick list of filenames in a directory.
+Echo the arguments to the -echo command.  Wildcards are expanded,
+so this is a convenient way to get a quick list of file names in a directory.
 The output is always terminated with a newline.
 .TP
-.B -ed [filename]
+.B -ed [fileName]
 Edit the specified file using line-mode commands.  The following
 .B ed
 commands are provided: = c r w i a d p l s f k z and q.
@@ -161,7 +193,7 @@ arithmetic combinations of these.  The substitute command and the
 search expression can only use literal strings.  There are some
 small differences in the way that some commands behave.
 .TP
-.B exec filename [args]
+.B exec fileName [args]
 Execute the specified program with the specified arguments.
 This replaces
 .B sash
@@ -171,10 +203,29 @@ completely by the executed program.
 Quit from
 .BR sash .
 .TP
-.B -grep [-in] word filename ...
+.B -file fileName ...
+Examine the specified files and print out their file type.
+This indicates whether the files are regular files or not,
+whether they contain printable text or shell scripts,
+are executables, or contain binary data.
+.TP
+.B -find dirName [-xdev] [-type chars] [-name pattern] [-size minSize]
+Find all files contained within the specified directory
+tree which meet all of the specified conditions.
+The -xdev option prevents crossing of mount points.
+The -name option specifies a wildcard pattern to match the last
+component of the file names.
+The -type option specifies that the files must have a type
+matching the specified list from the set: f d c b p s l.
+These represent regular files, directories, character devices,
+block devices, named pipes, sockets, and symbolic links.
+The -size option specifies that the files must be regular files or
+directories which contain at least the specified number of bytes.
+.TP
+.B -grep [-in] word fileName ...
 Display lines of the specified files which contain the given word.
-If only one filename is given, then only the matching lines are
-printed.  If multiple filenames are given, then the filenames are
+If only one file name is given, then only the matching lines are
+printed.  If multiple file names are given, then the file names are
 printed along with the matching lines.
 .I Word
 must be a single word,
@@ -182,7 +233,7 @@ must be a single word,
 ignored when doing the search.  If -n is given, then the line
 numbers of the matching lines are also printed.
 .TP
-.B -gunzip inputfilename ... [-o outputpath]
+.B -gunzip inputFileName ... [-o outputPath]
 Uncompress one or more files that had been compressed using the
 .I gzip
 or
@@ -202,16 +253,18 @@ Otherwise, the ".gz" or ".Z" extension is removed.
 If the -o option is given, then the input files will not be deleted,
 and the uncompressed versions of the files will be created as specified
 by
-.IR outputpath .
+.IR outputPath .
 If the output path is a directory, then the uncompressed versions of the
 input files will be placed in that directory with their file names
 modified as described above, or with the same name if the input file name
 does not have one of the special extensions.
-If the output path is not a directory, then only one input file is allowed,
+If the output path is a regular file, then only one input file is allowed,
 and the uncompressed version of that input file is created as the output
 path exactly as specified.
+If the output path is a block or character device, then the uncompressed
+versions of the input files are concatenated to the device.
 .TP
-.B -gzip inputfilename ... [-o outputpath]
+.B -gzip inputFileName ... [-o outputPath]
 Compresses one or more files using the
 .I gzip
 algorithm.
@@ -228,7 +281,7 @@ Otherwise, the ".gz" extension is added.
 If the -o option is given, then the input files will not be deleted,
 and the compressed versions of the files will be created as specified
 by
-.IR outputpath .
+.IR outputPath .
 If the output path is a directory, then the compressed versions of the
 input files will be placed in that directory with their file names
 modified as described above.
@@ -236,40 +289,55 @@ If the output path is not a directory, then only one input file is allowed,
 and the compressed version of that input file is created as the output
 path exactly as specified.
 .TP
-.B help
-Displays a list of built-in commands.
+.B help [word]
+Displays a list of built-in commands along with their usage strings.
+If a word is given,
+then just those commands whose name or usage contains the word is displayed.
+If a word is specified which exactly matches a built-in command name,
+then a short description of the command and its usage is given.
 .TP
 .B -kill [-signal] pid ...
 Sends the specified signal to the specified list of processes.
 .I Signal
-is a numberic value, or one of the special values HUP, INT,
-QUIT, or KILL.
+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 -ln [-s] srcname ... destname
+.B -ln [-s] srcName ... destName
 Links one or more files from the
-.I srcname
+.I srcName
 to the specified
-.IR destname .
+.IR destName .
 If there are
-multiple srcnames, or destname is a directory, then the link is
-put in the destname directory with the same name as the source name.
+multiple srcNames, or destName is a directory, then the link is
+put in the destName directory with the same name as the source name.
 The default links are hard links.  Using -s makes symbolic links.
-For symbolic links, only one srcname can be specified.
-.TP
-.B -ls [-lid] filename ...
-Display information about the specified filesnames, which may be
-directories.  The normal listing is simply a list of filenames,
-one per line.  The options available are -l, -i, and -d.  The -l
-option produces a long listing given the normal 'ls' information.
-The -i option also displays the inode numbers of the files.  The
--d option displays information about a directory, instead of the
+For symbolic links, only one srcName can be specified.
+.TP
+.B -ls [-lidFC] fileName ...
+Display information about the specified list of file names.
+The normal listing is simply a list of file names, one per line.
+The options available are -l, -i, -d, and -F.
+The -l option produces a long listing giving the normal 'ls' information.
+The -i option displays the inode numbers of the files.
+The -d option displays information about a directory, instead of the
 files within it.
-.TP
-.B -mkdir dirname ...
+The -F option appends a slash or asterisk to the file name if the file
+is a directory or is executable.
+The -C option displays the file names in a multi-column format.
+The width of the output is calculated using the COLS environment variable.
+.TP
+.B -lsattr fileName ...
+Display attributes for the specified files on the ext2 file system.
+The letter 'i' indicates that the file is immutable and cannot change.
+The letter 'a' indicates that the file is append-only.
+Dashes are shown where the attributes are not set.
+.TP
+.B -mkdir dirName ...
 Creates the specified directories.  They are created with the
 default permissions.
 .TP
-.B -mknod filename type major minor
+.B -mknod fileName type major minor
 Creates a special device node, either a character file or a block
 file.
 .I Filename
@@ -282,29 +350,29 @@ is the major device number.
 is the minor device number.
 Both of these numbers are decimal.
 .TP
-.B -more filename ...
-Type out the contents of the specified filenames, one page at a
+.B -more fileName ...
+Type out the contents of the specified file names, one page at a
 time.  For each page displayed, you can type 'n' and a return to go
 to the next file, 'q' and a return to quit the command completely,
 or just a return to go to the next page.  The environment variables
 LINES and COLS can be used to set the page size.
 .TP
-.B -mount [-t type] [-r] [-m] devname dirname
+.B -mount [-t type] [-r] [-m] devName dirName
 Mount a filesystem on a directory name.  The -t option specifies the
 type of filesystem being mounted, and defaults to "ext2".
 The -r option indicates to mount the filesystem read-only.
 The -m option indicates to remount an already mounted filesystem.
 .TP
-.B -mv srcname ... destname
+.B -mv srcName ... destName
 Moves one or more files from the
-.I srcname
+.I srcName
 to the
-.IR destname .
-If multiple srcnames are given, or if destname is a directory, then
-the srcnames are copied into the destination directory with the
-same names as the srcnames.  Renames are attempted first, but if
+.IR destName .
+If multiple srcNames are given, or if destName is a directory, then
+the srcNames are copied into the destination directory with the
+same names as the srcNames.  Renames are attempted first, but if
 this fails because of the files being on different filesystems,
-then copies and deletes are done instead.
+then copies and deletes are done instead.
 .TP
 .B -printenv [name]
 If
@@ -325,32 +393,41 @@ Prints the current working directory.
 Exits from
 .BR sash .
 .TP
-.B -rm filename ...
+.B -rm fileName ...
 Removes one or more files.
 .TP
-.B -rmdir dirname ...
+.B -rmdir dirName ...
 Removes one or more directories.  The directories must be empty
 for this to be successful.
 .TP
 .B setenv name value
 Set the value of an environment variable.
 .TP
-.B source filename
-Execute commands which are contained in the specified filename.
+.B source fileName
+Execute commands which are contained in the specified file name.
+.TP
+.B -sum fileName ...
+Calculates checksums for one or more files.
+This is the 16 bit checksum compatible with the BSD sum program.
 .TP
 .B -sync
 Do a "sync" system call to force dirty blocks out to the disk.
 .TP
-.B -tar [xtv]f devname [filename] ...
-List or restore files from a tar archive.  This command can only
-read tar files, not create them.  The available options are xtvf.
-The f option must be specified, and accepts a device or file
-name argument which contains the tar archive.  If no filename is
-given, all files in the archive are listed or extracted.  Otherwise,
-only those files starting with the specified filenames are done.
-Leading slashes in the tar archive filenames are removed.
-.TP
-.B -touch filename ...
+.B -tar [ctxv]f tarFileName [fileName] ...
+Create, list or extract files from a tar archive.
+The f option must be specified, and accepts a device or file name
+argument which contains the tar archive.
+When creating, at least one file name must be specified to be stored.
+If a file name is a directory, then all the files and directories
+within the directory are stored.
+Linked files and other special file types are not handled properly.
+When listing or extracting files, only those files starting with
+the specified file names are processed.
+If no file names are specified, then all files in the archive are processed.
+Leading slashes in the tar archive file names are always removed so that you
+might need to cd to "/" to restore files which had absolute paths.
+.TP
+.B -touch fileName ...
 Updates the modify times of the specifed files.  If a file does not
 exist, then it will be created with the default protection.
 .TP
@@ -363,8 +440,8 @@ permissions of newly created files.  If
 is not given, then the
 current umask value is printed.  The mask is an octal value.
 .TP
-.B -umount filename
-Unmounts a file system.  The filename can either be the device name
+.B -umount fileName
+Unmounts a file system.  The file name can either be the device name
 which is mounted, or else the directory name which the file system
 is mounted onto.
 .TP
@@ -388,14 +465,58 @@ The -q option 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
+that they replace the corresponding standard commands.
+This is the same result as if the 'aliasall' command was used.
+.SH SYSTEM RECOVERY
+This section contains some useful information about using
+.B sash
+with
+.B lilo
+to perform system recovery in some situations.
+.PP
+When important shared libraries are being upgraded,
+it might be a good idea to have
+.B sash
+already running on a console by itself.
+Then if there is a problem with the shared libraries
+.B sash
+will be unaffected and you may be able to use it to fix the problem.
+.PP
+If a problem with the system shows up at boot time so that you cannot
+enter multi-user mode and log in,
+then you can first try booting into single-user mode by adding the
+.I single
+keyword after your kernel image name at the
+.B lilo
+prompt.
+If you manage to reach a shell prompt, then you can run
+.B sash
+from that shell (if necessary).
+One reason for doing this is that you might need to use the
+.B -mount
+command with the -m option to remount the root file system
+so that it can be modified.
+.PP
+If you cannot reach the shell in single-user mode,
+then you can try running sash directly as a replacement for the init process.
+This is done by adding the
+.I init=/bin/sash
+keyword after your kernel image name at the
+.B lilo
+prompt.
+When this is done, then the use of the
+.B aliasall
+command might be useful to reduce attempts to access the root file system
+when running commands.
+.PP
+If your root file system is so corrupted that you cannot get
+.B sash
+to run at all, then you will have to resort to a system recovery floppy.
 .SH WARNINGS
 .B Sash
-should obviously be linked statically, otherwise it's purpose is lost.
-.PP
-The system is still vulnerable to unrunnable shared versions of
-.B init
-and
-.B sh.
+should obviously be linked statically, otherwise its purpose is lost.
 .PP
 Several other system commands might be necessary for system recovery,
 but aren't built-in to
@@ -404,5 +525,5 @@ but aren't built-in to
 .nf
 David I. Bell
 dbell@canb.auug.org.au
-March 8, 1998
+2 October 1999
 .fi
diff --git a/sash.c b/sash.c
index 51cdb91fec880226cc0014aacc85a9c87790db3d..3cde006628dde9653337fc236eb6ae38017ed7b7 100644 (file)
--- a/sash.c
+++ b/sash.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
  * This program should NOT be built using shared libraries.
  */
 
-#include <wait.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <signal.h>
 #include <errno.h>
 
 #include "sash.h"
 
 
-static const char * const      version = "2.1";
+static const char * const      version = "3.4";
 
 
-typedef struct {
-       char *  name;
-       char *  usage;
-       void    (*func) PROTO((int argc, const char ** argv));
-       int     minargs;
-       int     maxargs;
-} CMDTAB;
+/*
+ * The special maximum argument value which means that there is
+ * no limit to the number of arguments on the command line.
+ */
+#define        INFINITE_ARGS   0x7fffffff
 
 
-static const CMDTAB    cmdtab[] =
+/*
+ * One entry of the command table.
+ */
+typedef struct
 {
-       {"alias",       "[name [command]]",     do_alias,
-       1,              MAXARGS},
+       const char *    name;
+       void            (*func)(int argc, const char ** argv);
+       int             minArgs;
+       int             maxArgs;
+       const char *    description;
+       const char *    usage;
+} CommandEntry;
+
+
+/*
+ * The table of built-in commands.
+ * This is terminated wih an entry containing NULL values.
+ */
+static const CommandEntry      commandEntryTable[] =
+{
+       {
+               "alias",        do_alias,       1,      INFINITE_ARGS,
+               "Define a command alias",
+               "[name [command]]"
+       },
+
+       {
+               "aliasall",     do_aliasall,    1,      1,
+               "Define aliases for all of the build-in commands",
+               ""
+       },
+
+       {
+               "-ar",          do_ar,          3,      INFINITE_ARGS,
+               "Extract or list files from an AR file",
+               "[txp]v arFileName fileName ..."
+       },
 
-       {"cd",          "[dirname]",            do_cd,
-       1,              2},
+       {
+               "cd",           do_cd,          1,      2,
+               "Change current directory",
+               "[dirName]"
+       },
 
-       {"-chgrp",      "gid filename ...",     do_chgrp,
-       3,              MAXARGS},
+#ifdef HAVE_EXT2
+       {
+               "-chattr",      do_chattr,      3,      INFINITE_ARGS,
+               "Change ext2 file attributes",
+               "[+i] [-i] [+a] [-a] fileName ..."
+       },
+#endif
 
-       {"-chmod",      "mode filename ...",    do_chmod,
-       3,              MAXARGS},
+       {
+               "-chgrp",       do_chgrp,       3,      INFINITE_ARGS,
+               "Change the group id of some files",
+               "gid fileName ..."
+       },
 
-       {"-chown",      "uid filename ...",     do_chown,
-       3,              MAXARGS},
+       {
+               "-chmod",       do_chmod,       3,      INFINITE_ARGS,
+               "Change the protection of some files",
+               "mode fileName ..."
+       },
 
-       {"-cmp",                "filename1 filename2",  do_cmp,
-       3,              3},
+       {
+               "-chown",       do_chown,       3,      INFINITE_ARGS,
+               "Change the owner id of some files",
+               "uid fileName ..."
+       },
 
-       {"-cp",         "srcname ... destname", do_cp,
-       3,              MAXARGS},
+       {
+               "-cmp",         do_cmp,         3,      3,
+               "Compare two files for equality",
+               "fileName1 fileName2"
+       },
 
-       {"-dd",         "if=name of=name [bs=n] [count=n] [skip=n] [seek=n]", do_dd,
-       3,              MAXARGS},
+       {
+               "-cp",          do_cp,          3,      INFINITE_ARGS,
+               "Copy files",
+               "srcName ... destName"
+       },
+
+       {
+               "-dd",          do_dd,          3,      INFINITE_ARGS,
+               "Copy data between two files",
+               "if=name of=name [bs=n] [count=n] [skip=n] [seek=n]"
+       },
+
+       {
+               "-echo",        do_echo,        1,      INFINITE_ARGS,
+               "Echo the arguments",
+               "[args] ..."
+       },
+
+       {
+               "-ed",          do_ed,          1,      2,
+               "Edit a fileName using simple line mode commands",
+               "[fileName]"
+       },
 
-       {"-echo",       "[args] ...",           do_echo,
-       1,              MAXARGS},
+       {
+               "exec",         do_exec,        2,      INFINITE_ARGS,
+               "Execute another program in place of this sash process",
+               "fileName [args]"
+       },
 
-       {"-ed",         "[filename]",           do_ed,
-       1,              2},
+       {
+               "exit",         do_exit,        1,      1,
+               "Exit from sash",
+               ""
+       },
 
-       {"exec",                "filename [args]",      do_exec,
-       2,              MAXARGS},
+       {
+               "-file",        do_file,        1,      INFINITE_ARGS,
+               "Describe information about files",
+               "fileName ..."
+       },
 
-       {"exit",                "",                     do_exit,
-       1,              1},
+       {
+               "-find",        do_find,        2,      INFINITE_ARGS,
+               "Find files in a directory tree meeting some conditions",
+               "dirName [-xdev] [-type chars] [-name pattern] [-size minSize]"
+       },
 
-       {"-grep",       "[-in] word filename ...",      do_grep,
-       3,              MAXARGS},
+       {
+               "-grep",        do_grep,        3,      INFINITE_ARGS,
+               "Look for lines containing a word in some files",
+               "[-in] word fileName ..."
+       },
 
 #ifdef HAVE_GZIP
-       {"-gunzip",     "filename ... [-o outputPath]", do_gunzip,
-       2,              MAXARGS},
+       {
+               "-gunzip",      do_gunzip,      2,      INFINITE_ARGS,
+               "Uncompress files which were saved in GZIP or compress format",
+               "fileName ... [-o outputPath]"
+       },
 
-       {"-gzip",       "filename ... [-o outputPath]", do_gzip,
-       2,              MAXARGS},
+       {
+               "-gzip",        do_gzip,        2,      INFINITE_ARGS,
+               "Compress files into GZIP format",
+               "fileName ... [-o outputPath]"
+       },
 #endif
 
-       {"help",                "[word]",               do_help,
-       1,              2},
+       {
+               "help",         do_help,        1,      2,
+               "Print help about a command",
+               "[word]"
+       },
 
-       {"-kill",       "[-sig] pid ...",       do_kill,
-       2,              MAXARGS},
+       {
+               "-kill",        do_kill,        2,      INFINITE_ARGS,
+               "Send a signal to the specified process",
+               "[-sig] pid ..."
+       },
+
+       {
+               "-ln",          do_ln,          3,      INFINITE_ARGS,
+               "Link one fileName to another",
+               "[-s] srcName ... destName"
+       },
 
-       {"-ln",         "[-s] srcname ... destname",    do_ln,
-       3,              MAXARGS},
+       {
+               "-ls",          do_ls,          1,      INFINITE_ARGS,
+               "List information about files or directories",
+               "[-lidFC] fileName ..."
+       },
+
+#ifdef HAVE_EXT2
+       {
+               "-lsattr",      do_lsattr,      2,      INFINITE_ARGS,
+               "List ext2 file attributes",
+               "fileName ..."
+       },
+#endif
 
-       {"-ls",         "[-lid] filename ...",  do_ls,
-       1,              MAXARGS},
+       {
+               "-mkdir",       do_mkdir,       2,      INFINITE_ARGS,
+               "Create a directory",
+               "dirName ..."
+       },
 
-       {"-mkdir",      "dirname ...",          do_mkdir,
-       2,              MAXARGS},
+       {
+               "-mknod",       do_mknod,       5,      5,
+               "Create a special type of file",
+               "fileName type major minor"
+       },
 
-       {"-mknod",      "filename type major minor",    do_mknod,
-       5,              5},
+       {
+               "-more",        do_more,        2,      INFINITE_ARGS,
+               "Type file contents page by page",
+               "fileName ..."
+       },
 
-       {"-more",       "filename ...",         do_more,
-       2,              MAXARGS},
+       {
+               "-mount",       do_mount,       3,      INFINITE_ARGS,
+               "Mount or remount a filesystem on a directory",
+               "[-t type] [-r] [-m] devName dirName"
+       },
 
-       {"-mount",      "[-t type] [-r] [-m] devname dirname",  do_mount,
-       3,              MAXARGS},
+       {
+               "-mv",          do_mv,          3,      INFINITE_ARGS,
+               "Move or rename files",
+               "srcName ... destName"
+       },
 
-       {"-mv",         "srcname ... destname", do_mv,
-       3,              MAXARGS},
+       {
+               "-printenv",    do_printenv,    1,      2,
+               "Print environment variables",
+               "[name]"
+       },
 
-       {"-printenv",   "[name]",               do_printenv,
-       1,              2},
+       {
+               "prompt",       do_prompt,      2,      INFINITE_ARGS,
+               "Set the prompt string for sash",
+               "string"
+       },
 
-       {"prompt",      "string",               do_prompt,
-       2,              MAXARGS},
+       {
+               "-pwd",         do_pwd,         1,      1,
+               "Print the current working directory",
+               ""
+       },
 
-       {"-pwd",                "",                     do_pwd,
-       1,              1},
+       {
+               "quit",         do_exit,        1,      1,
+               "Exit from sash",
+               ""
+       },
 
-       {"quit",                "",                     do_exit,
-       1,              1},
+       {
+               "-rm",          do_rm,          2,      INFINITE_ARGS,
+               "Remove the specified files",
+               "fileName ..."
+       },
 
-       {"-rm",         "filename ...",         do_rm,
-       2,              MAXARGS},
+       {
+               "-rmdir",       do_rmdir,       2,      INFINITE_ARGS,
+               "Remove the specified empty directories",
+               "dirName ..."
+       },
 
-       {"-rmdir",      "dirname ...",          do_rmdir,
-       2,              MAXARGS},
+       {
+               "setenv",       do_setenv,      3,      3,
+               "Set an environment variable value",
+               "name value"
+       },
 
-       {"setenv",      "name value",           do_setenv,
-       3,              3},
+       {
+               "source",       do_source,      2,      2,
+               "Read commands from the specified file",
+               "fileName"
+       },
 
-       {"source",      "filename",             do_source,
-       2,              2},
+       {
+               "-sum",         do_sum,         2,      INFINITE_ARGS,
+               "Calculate checksums of the specified files",
+               "fileName ..."
+       },
 
-       {"-sync",       "",                     do_sync,
-       1,              1},
+       {
+               "-sync",        do_sync,        1,      1,
+               "Sync the disks to force cached data to them",
+               ""
+       },
 
-       {"-tar",                "[xtv]f devname filename ...",  do_tar,
-       2,              MAXARGS},
+       {
+               "-tar",         do_tar,         2,      INFINITE_ARGS,
+               "Create, extract, or list files from a TAR file",
+               "[cxtv]f tarFileName fileName ..."
+       },
 
-       {"-touch",      "filename ...",         do_touch,
-       2,              MAXARGS},
+       {
+               "-touch",       do_touch,       2,      INFINITE_ARGS,
+               "Update times or create the specified files",
+               "fileName ..."
+       },
 
-       {"umask",       "[mask]",               do_umask,
-       1,              2},
+       {
+               "umask",        do_umask,       1,      2,
+               "Set the umask value for file protections",
+               "[mask]"
+       },
 
-       {"-umount",     "filename",             do_umount,
-       2,              2},
+       {
+               "-umount",      do_umount,      2,      2,
+               "Unmount a filesystem",
+               "fileName"
+       },
 
-       {"unalias",     "name",                 do_unalias,
-       2,              2},
+       {
+               "unalias",      do_unalias,     2,      2,
+               "Remove a command alias",
+               "name"
+       },
 
-       {"-where",      "program",              do_where,
-       2,              2},
+       {
+               "-where",       do_where,       2,      2,
+               "Type the location of a program",
+               "program"
+       },
 
-       {NULL,          0,                      0,
-       0,              0}
+       {
+               NULL,           0,              0,      0,
+               NULL,
+               NULL
+       }
 };
 
 
-typedef struct {
+/*
+ * The definition of an command alias.
+ */
+typedef struct
+{
        char *  name;
        char *  value;
-} ALIAS;
+} Alias;
 
 
 /*
  * Local data.
  */
-static ALIAS * aliastable;
-static int     aliascount;
+static Alias * aliasTable;
+static int     aliasCount;
 
-static FILE *  sourcefiles[MAXSOURCE];
-static int     sourcecount;
+static FILE *  sourcefiles[MAX_SOURCE];
+static int     sourceCount;
 
-static BOOL    intcrlf = TRUE;
+static BOOL    intCrlf = TRUE;
 static char *  prompt;
 
 
 /*
  * Local procedures.
  */
-static void    catchint PROTO((int));
-static void    catchquit PROTO((int));
-static void    readfile PROTO((const char * name));
-static void    command PROTO((const char * cmd));
-static void    runcmd PROTO((const char * cmd, int argc, const char ** argv));
-static void    showprompt PROTO((void));
-static void    usage PROTO((void));
-static BOOL    trybuiltin PROTO((int argc, const char ** argv));
-static ALIAS * findalias PROTO((const char * name));
+static void    catchInt(int);
+static void    catchQuit(int);
+static void    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 Alias * findAlias(const char * name);
 
 
 /*
  * Global interrupt flag.
  */
-BOOL   intflag;
+BOOL   intFlag;
 
 
 int
-main(argc, argv)
-       int             argc;
-       const char **   argv;
+main(int argc, const char ** argv)
 {
        const char *    cp;
        const char *    singleCommand;
        BOOL            quietFlag;
-       char            buf[PATHLEN];
+       BOOL            aliasFlag;
+       char            buf[PATH_LEN];
 
        singleCommand = NULL;
        quietFlag = FALSE;
+       aliasFlag = FALSE;
 
        /*
         * Look for options.
@@ -248,6 +436,10 @@ main(argc, argv)
                                quietFlag = TRUE;
                                break;
 
+                       case 'a':
+                               aliasFlag = TRUE;
+                               break;
+
                        case 'h':
                        case '?':
                                usage();
@@ -272,6 +464,12 @@ main(argc, argv)
        if (getenv("PATH") == NULL)
                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 we are to execute a single command, then do so and exit.
         */
@@ -286,26 +484,35 @@ main(argc, argv)
         * Print a hello message unless we are told to be silent.
         */
        if (!quietFlag && isatty(STDIN))
+       {
                printf("Stand-alone shell (version %s)\n", version);
 
-       signal(SIGINT, catchint);
-       signal(SIGQUIT, catchquit);
+               if (aliasFlag)
+                       printf("Built-in commands are aliased to standard commands\n");
+       }
 
-       if (getenv("PATH") == NULL)
-               putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc");
+       signal(SIGINT, catchInt);
+       signal(SIGQUIT, catchQuit);
 
+       /*
+        * Execute the user's alias file if present.
+        */
        cp = getenv("HOME");
 
-       if (cp) {
+       if (cp)
+       {
                strcpy(buf, cp);
                strcat(buf, "/");
                strcat(buf, ".aliasrc");
 
                if ((access(buf, 0) == 0) || (errno != ENOENT))
-                       readfile(buf);
+                       readFile(buf);
        }
 
-       readfile(NULL);
+       /*
+        * Read commands from stdin.
+        */
+       readFile(NULL);
 
        return 0;
 }
@@ -316,15 +523,15 @@ main(argc, argv)
  * A null name pointer indicates to read from stdin.
  */
 static void
-readfile(name)
-       const char *    name;
+readFile(const char * name)
 {
        FILE *  fp;
        int     cc;
-       BOOL    ttyflag;
-       char    buf[CMDLEN];
+       BOOL    ttyFlag;
+       char    buf[CMD_LEN];
 
-       if (sourcecount >= MAXSOURCE) {
+       if (sourceCount >= MAX_SOURCE)
+       {
                fprintf(stderr, "Too many source files\n");
 
                return;
@@ -332,33 +539,39 @@ readfile(name)
 
        fp = stdin;
 
-       if (name) {
+       if (name)
+       {
                fp = fopen(name, "r");
 
-               if (fp == NULL) {
+               if (fp == NULL)
+               {
                        perror(name);
 
                        return;
                }
        }
 
-       sourcefiles[sourcecount++] = fp;
+       sourcefiles[sourceCount++] = fp;
 
-       ttyflag = isatty(fileno(fp));
+       ttyFlag = isatty(fileno(fp));
 
-       while (TRUE) {
-               if (ttyflag)
-                       showprompt();
+       while (TRUE)
+       {
+               if (ttyFlag)
+                       showPrompt();
 
-               if (intflag && !ttyflag && (fp != stdin)) {
+               if (intFlag && !ttyFlag && (fp != stdin))
+               {
                        fclose(fp);
-                       sourcecount--;
+                       sourceCount--;
 
                        return;
                }
        
-               if (fgets(buf, CMDLEN - 1, fp) == NULL) {
-                       if (ferror(fp) && (errno == EINTR)) {
+               if (fgets(buf, CMD_LEN - 1, fp) == NULL)
+               {
+                       if (ferror(fp) && (errno == EINTR))
+                       {
                                clearerr(fp);
 
                                continue;
@@ -372,7 +585,7 @@ readfile(name)
                if (buf[cc - 1] == '\n')
                        cc--;
 
-               while ((cc > 0) && isblank(buf[cc - 1]))
+               while ((cc > 0) && isBlank(buf[cc - 1]))
                        cc--;
 
                buf[cc] = '\0';
@@ -380,7 +593,8 @@ readfile(name)
                command(buf);
        }
 
-       if (ferror(fp)) {
+       if (ferror(fp))
+       {
                perror("Reading command line");
 
                if (fp == stdin)
@@ -392,7 +606,7 @@ readfile(name)
        if (fp != stdin)
                fclose(fp);
 
-       sourcecount--;
+       sourceCount--;
 }
 
 
@@ -402,60 +616,74 @@ readfile(name)
  * command is an alias, and expands wildcards.
  */
 static void
-command(cmd)
-       const char *    cmd;
+command(const char * cmd)
 {
-       char *          cp;
-       const ALIAS *   alias;
-       const char **   argv;
-       int             argc;
-       char            buf[CMDLEN];
+       const char *    endCmd;
+       const Alias *   alias;
+       char            newCommand[CMD_LEN];
+       char            cmdName[CMD_LEN];
 
-       intflag = FALSE;
+       /*
+        * Rest the interrupt flag and free any memory chunks that
+        * were allocated by the previous command.
+        */
+       intFlag = FALSE;
 
-       freechunks();
+       freeChunks();
 
-       while (isblank(*cmd))
+       /*
+        * Skip leading blanks.
+        */
+       while (isBlank(*cmd))
                cmd++;
 
-       if ((*cmd == '\0') || (*cmd == '#') || !makeargs(cmd, &argc, &argv))
+       /*
+        * If the command is empty or is a comment then ignore it.
+        */
+       if ((*cmd == '\0') || (*cmd == '#'))
                return;
 
        /*
-        * Search for the command in the alias table.
-        * If it is found, then replace the command name with
-        * the alias, and append any other arguments to it.
+        * Look for the end of the command name and then copy the
+        * command name to a buffer so we can null terminate it.
         */
-       alias = findalias(argv[0]);
+       endCmd = cmd;
 
-       if (alias) {
-               cp = buf;
-               strcpy(cp, alias->value);
-               cp += strlen(cp);
+       while (*endCmd && !isBlank(*endCmd))
+               endCmd++;
 
-               while (--argc > 0) {
-                       *cp++ = ' ';
-                       strcat(cp, *++argv);
-                       cp += strlen(cp);
-               }
+       memcpy(cmdName, cmd, endCmd - cmd);
 
-               cmd = buf;
+       cmdName[endCmd - cmd] = '\0';
 
-               if (!makeargs(cmd, &argc, &argv))
-                       return;
+       /*
+        * Search for the command name in the alias table.
+        * If it is found, then replace the command name with
+        * the alias value, and append the current command
+        * line arguments to that.
+        */
+       alias = findAlias(cmdName);
+
+       if (alias)
+       {
+               strcpy(newCommand, alias->value);
+               strcat(newCommand, endCmd);
+
+               cmd = newCommand;
        }
 
        /*
         * Now look for the command in the builtin table, and execute
         * the command if found.
         */
-       if (trybuiltin(argc, argv))
+       if (tryBuiltIn(cmd))
                return;
 
        /*
-        * Not found, run the program along the PATH list.
+        * The command is not a built-in, so run the program along
+        * the PATH list.
         */
-       runcmd(cmd, argc, argv);
+       runCmd(cmd);
 }
 
 
@@ -465,114 +693,105 @@ command(cmd)
  * command succeeds.  Returns FALSE if this is not a built-in command.
  */
 static BOOL
-trybuiltin(argc, argv)
-       int             argc;
-       const char **   argv;
+tryBuiltIn(const char * cmd)
 {
-       const CMDTAB *  cmdptr;
-       int             oac;
-       int             newargc;
-       int             matches;
-       int             i;
-       const char *    newargv[MAXARGS];
-       const char *    nametable[MAXARGS];
+       const char *            endCmd;
+       const CommandEntry *    entry;
+       int                     argc;
+       const char **           argv;
+       char                    cmdName[CMD_LEN];
 
-       cmdptr = cmdtab - 1;
+       /*
+        * 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;
 
-       do {
-               cmdptr++;
+       while (*endCmd && !isBlank(*endCmd))
+               endCmd++;
 
-               if (cmdptr->name == NULL)
-                       return FALSE;
+       memcpy(cmdName, cmd, endCmd - cmd);
 
-       } while (strcmp(argv[0], cmdptr->name));
+       cmdName[endCmd - cmd] = '\0';
 
        /*
-        * Give a usage string if the number of arguments is too large
-        * or too small.
+        * Search the command table looking for the command name.
         */
-       if ((argc < cmdptr->minargs) || (argc > cmdptr->maxargs)) {
-               fprintf(stderr, "usage: %s %s\n",
-                       cmdptr->name, cmdptr->usage);
-
-               return TRUE;
+       for (entry = commandEntryTable; entry->name != NULL; entry++)
+       {
+               if (strcmp(entry->name, cmdName) == 0)
+                       break;
        }
 
        /*
-        * Check here for several special commands which do not
-        * have wildcarding done for them.
+        * If the command is not a built-in, return indicating that.
         */
-       if ((cmdptr->func == do_alias) || (cmdptr->func == do_prompt)) {
-               (*cmdptr->func)(argc, argv);
+       if (entry->name == NULL)
+               return FALSE;
 
+       /*
+        * The command is a built-in.
+        * Break the command up into arguments and expand wildcards.
+        */
+       if (!makeArgs(cmd, &argc, &argv))
                return TRUE;
-       }
 
        /*
-        * Now for each command argument, see if it is a wildcard, and if
-        * so, replace the argument with the list of matching filenames.
+        * Give a usage string if the number of arguments is too large
+        * or too small.
         */
-       newargv[0] = argv[0];
-       newargc = 1;
-       oac = 0;
-
-       while (++oac < argc) {
-               matches = expandwildcards(argv[oac], MAXARGS, nametable);
-
-               if (matches < 0)
-                       return TRUE;
-
-               if ((newargc + matches) >= MAXARGS) {
-                       fprintf(stderr, "Too many arguments\n");
-
-                       return TRUE;
-               }
-
-               if (matches == 0)
-                       newargv[newargc++] = argv[oac];
+       if ((argc < entry->minArgs) || (argc > entry->maxArgs))
+       {
+               fprintf(stderr, "usage: %s %s\n", entry->name, entry->usage);
 
-               for (i = 0; i < matches; i++)
-                       newargv[newargc++] = nametable[i];
+               return TRUE;
        }
 
-       (*cmdptr->func)(newargc, newargv);
+       /*
+        * Call the built-in function with the argument list.
+        */
+       entry->func(argc, argv);
 
        return TRUE;
 }
 
 
 /*
- * Execute the specified command.
+ * Execute the specified command either by forking and executing
+ * the program ourself, or else by using the shell.
  */
 static void
-runcmd(cmd, argc, argv)
-       const char *    cmd;
-       int             argc;
-       const char **   argv;
+runCmd(const char * cmd)
 {
        const char *    cp;
        BOOL            magic;
-       int             pid;
+       pid_t           pid;
        int             status;
 
+       /*
+        * Check the command for any magic shell characters
+        * except for quoting.
+        */
        magic = FALSE;
 
-       for (cp = cmd; *cp; cp++) {
+       for (cp = cmd; *cp; cp++)
+       {
                if ((*cp >= 'a') && (*cp <= 'z'))
                        continue;
 
                if ((*cp >= 'A') && (*cp <= 'Z'))
                        continue;
 
-               if (isdecimal(*cp))
+               if (isDecimal(*cp))
                        continue;
 
-               if (isblank(*cp))
+               if (isBlank(*cp))
                        continue;
 
                if ((*cp == '.') || (*cp == '/') || (*cp == '-') ||
                        (*cp == '+') || (*cp == '=') || (*cp == '_') ||
-                       (*cp == ':') || (*cp == ','))
+                       (*cp == ':') || (*cp == ',') || (*cp == '\'') ||
+                       (*cp == '"'))
                {
                        continue;
                }
@@ -580,100 +799,175 @@ runcmd(cmd, argc, argv)
                magic = TRUE;
        }
 
-       if (magic) {
+       /*
+        * If there were any magic characters used then run the
+        * command using the shell.
+        */
+       if (magic)
+       {
                system(cmd);
 
                return;
        }
 
        /*
-        * No magic characters in the command, so do the fork and
-        * exec ourself.  If this fails with ENOEXEC, then run the
-        * shell anyway since it might be a shell script.
+        * No magic characters were in the command, so we can do the fork
+        * and exec ourself.
         */
        pid = fork();
 
-       if (pid < 0) {
+       if (pid < 0)
+       {
                perror("fork failed");
 
                return;
        }
 
-       if (pid) {
-               status = 0;
-               intcrlf = FALSE;
+       /*
+        * If we are the child process, then go execute the program.
+        */
+       if (pid == 0)
+               childProcess(cmd);
 
-               while (((pid = wait(&status)) < 0) && (errno == EINTR))
-                       ;
+       /*
+        * We are the parent process.
+        * Wait for the child to complete.
+        */
+       status = 0;
+       intCrlf = FALSE;
 
-               intcrlf = TRUE;
+       while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR))
+               ;
 
-               if ((status & 0xff) == 0)
-                       return;
+       intCrlf = TRUE;
 
-               fprintf(stderr, "pid %d: %s (signal %d)\n", pid,
-                       (status & 0x80) ? "core dumped" : "killed",
-                       status & 0x7f);
+       if (pid < 0)
+       {
+               fprintf(stderr, "Error from waitpid: %s", strerror(errno));
 
                return;
        }
 
+       if (WIFSIGNALED(status))
+       {
+               fprintf(stderr, "pid %ld: killed by signal %d\n",
+                       (long) pid, WTERMSIG(status));
+       }
+}
+
+
+/*
+ * Here as the child process to try to execute the command.
+ * This is only called if there are no meta-characters in the command.
+ * This procedure never returns.
+ */
+static void
+childProcess(const char * cmd)
+{
+       const char **   argv;
+       int             argc;
+
        /*
-        * We are the child, so run the program.
-        * First close any extra file descriptors we have opened.
+        * Close any extra file descriptors we have opened.
         */     
-       while (--sourcecount >= 0) {
-               if (sourcefiles[sourcecount] != stdin)
-                       fclose(sourcefiles[sourcecount]);
+       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);
+       }
+
+       /*
+        * Try to execute the program directly.
+        */
        execvp(argv[0], (char **) argv);
 
-       if (errno == ENOEXEC) {
+       /*
+        * 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);
        }
 
+       /*
+        * There was something else wrong, complain and exit.
+        */
        perror(argv[0]);
        exit(1);
 }
 
 
 void
-do_help(argc, argv)
-       int             argc;
-       const char **   argv;
+do_help(int argc, const char ** argv)
 {
-       const CMDTAB *  cmdptr;
-       const char *    str;
+       const CommandEntry *    entry;
+       const char *            str;
 
        str = NULL;
 
        if (argc == 2)
                str = argv[1];
 
-       for (cmdptr = cmdtab; cmdptr->name; cmdptr++) {
-               if ((str == NULL) || (strstr(cmdptr->name, str) != NULL))
-                       printf("%-10s %s\n", cmdptr->name, cmdptr->usage);
+       /*
+        * Check for an exact match, in which case describe the program.
+        */
+       if (str)
+       {
+               for (entry = commandEntryTable; entry->name; entry++)
+               {
+                       if (strcmp(str, entry->name) == 0)
+                       {
+                               printf("%s\n", entry->description);
+
+                               printf("usage: %s %s\n", entry->name,
+                                       entry->usage);
+
+                               return;
+                       }
+               }
+       }
+
+       /*
+        * Print short information about commands which contain the
+        * specified word.
+        */
+       for (entry = commandEntryTable; entry->name; entry++)
+       {
+               if ((str == NULL) || (strstr(entry->name, str) != NULL) ||
+                       (strstr(entry->usage, str) != NULL))
+               {
+                       printf("%-10s %s\n", entry->name, entry->usage);
+               }
        }
 }
 
 
 void
-do_alias(argc, argv)
-       int             argc;
-       const char **   argv;
+do_alias(int argc, const char ** argv)
 {
        const char *    name;
-       char *  value;
-       ALIAS * alias;
-       int     count;
-       char    buf[CMDLEN];
+       char *          value;
+       Alias *         alias;
+       int             count;
+       char            buf[CMD_LEN];
 
-       if (argc < 2) {
-               count = aliascount;
+       if (argc < 2)
+       {
+               count = aliasCount;
 
-               for (alias = aliastable; count-- > 0; alias++)
+               for (alias = aliasTable; count-- > 0; alias++)
                        printf("%s\t%s\n", alias->name, alias->value);
 
                return;
@@ -681,8 +975,9 @@ do_alias(argc, argv)
 
        name = argv[1];
 
-       if (argc == 2) {
-               alias = findalias(name);
+       if (argc == 2)
+       {
+               alias = findAlias(name);
 
                if (alias)
                        printf("%s\n", alias->value);
@@ -692,18 +987,20 @@ do_alias(argc, argv)
                return; 
        }
 
-       if (strcmp(name, "alias") == 0) {
+       if (strcmp(name, "alias") == 0)
+       {
                fprintf(stderr, "Cannot alias \"alias\"\n");
 
                return;
        }
 
-       if (!makestring(argc - 2, argv + 2, buf, CMDLEN))
+       if (!makeString(argc - 2, argv + 2, buf, CMD_LEN))
                return;
 
        value = malloc(strlen(buf) + 1);
 
-       if (value == NULL) {
+       if (value == NULL)
+       {
                fprintf(stderr, "No memory for alias value\n");
 
                return;
@@ -711,39 +1008,45 @@ do_alias(argc, argv)
 
        strcpy(value, buf);
 
-       alias = findalias(name);
+       alias = findAlias(name);
 
-       if (alias) {
+       if (alias)
+       {
                free(alias->value);
                alias->value = value;
 
                return;
        }
 
-       if ((aliascount % ALIASALLOC) == 0) {
-               count = aliascount + ALIASALLOC;
+       if ((aliasCount % ALIAS_ALLOC) == 0)
+       {
+               count = aliasCount + ALIAS_ALLOC;
 
-               if (aliastable)
-                       alias = (ALIAS *) realloc(aliastable,
-                               sizeof(ALIAS *) * count);
+               if (aliasTable)
+               {
+                       alias = (Alias *) realloc(aliasTable,
+                               sizeof(Alias) * count);
+               }
                else
-                       alias = (ALIAS *) malloc(sizeof(ALIAS *) * count);
+                       alias = (Alias *) malloc(sizeof(Alias) * count);
 
-               if (alias == NULL) {
+               if (alias == NULL)
+               {
                        free(value);
                        fprintf(stderr, "No memory for alias table\n");
 
                        return;
                }
 
-               aliastable = alias;
+               aliasTable = alias;
        }
 
-       alias = &aliastable[aliascount];
+       alias = &aliasTable[aliasCount];
 
        alias->name = malloc(strlen(name) + 1);
 
-       if (alias->name == NULL) {
+       if (alias->name == NULL)
+       {
                free(value);
                fprintf(stderr, "No memory for alias name\n");
 
@@ -752,7 +1055,35 @@ do_alias(argc, argv)
 
        strcpy(alias->name, name);
        alias->value = value;
-       aliascount++;
+       aliasCount++;
+}
+
+
+/*
+ * Build aliases for all of the built-in commands which start with a dash,
+ * using the names without the dash.
+ */
+void
+do_aliasall(int argc, const char **argv)
+{
+       const CommandEntry *    entry;
+       const char *            name;
+       const char *            newArgv[4];
+
+       for (entry = commandEntryTable; entry->name; entry++)
+       {
+               name = entry->name;
+
+               if (*name != '-')
+                       continue;
+
+               newArgv[0] = "alias";
+               newArgv[1] = name + 1;
+               newArgv[2] = name;
+               newArgv[3] = NULL;
+
+               do_alias(3, newArgv);
+       }
 }
 
 
@@ -760,16 +1091,16 @@ do_alias(argc, argv)
  * Look up an alias name, and return a pointer to it.
  * Returns NULL if the name does not exist.
  */
-static ALIAS *
-findalias(name)
-       const char *    name;
+static Alias *
+findAlias(const char * name)
 {
-       ALIAS * alias;
+       Alias * alias;
        int     count;
 
-       count = aliascount;
+       count = aliasCount;
 
-       for (alias = aliastable; count-- > 0; alias++) {
+       for (alias = aliasTable; count-- > 0; alias++)
+       {
                if (strcmp(name, alias->name) == 0)
                        return alias;
        }
@@ -779,55 +1110,52 @@ findalias(name)
 
 
 void
-do_source(argc, argv)
-       int             argc;
-       const char **   argv;
+do_source(int argc, const char ** argv)
 {
-       readfile(argv[1]);
+       readFile(argv[1]);
 }
 
 
 void
-do_exec(argc, argv)
-       int             argc;
-       const char **   argv;
+do_exec(int argc, const char ** argv)
 {
        const char *    name;
 
        name = argv[1];
 
-       if (access(name, 4)) {
+       if (access(name, 4))
+       {
                perror(name);
 
                return;
        }
 
-       while (--sourcecount >= 0) {
-               if (sourcefiles[sourcecount] != stdin)
-                       fclose(sourcefiles[sourcecount]);
+       while (--sourceCount >= 0)
+       {
+               if (sourcefiles[sourceCount] != stdin)
+                       fclose(sourcefiles[sourceCount]);
        }
 
        argv[argc] = NULL;
 
-       execv(name, (char **) argv + 1);
+       execvp(name, (char **) argv + 1);
        exit(1);
 }
 
 
 void
-do_prompt(argc, argv)
-       int             argc;
-       const char **   argv;
+do_prompt(int argc, const char ** argv)
 {
        char *  cp;
-       char    buf[CMDLEN];
+       char    buf[CMD_LEN];
 
-       if (!makestring(argc - 1, argv + 1, buf, CMDLEN))
+       if (!makeString(argc - 1, argv + 1, buf, CMD_LEN))
                return;
 
        cp = malloc(strlen(buf) + 2);
 
-       if (cp == NULL) {
+       if (cp == NULL)
+       {
                fprintf(stderr, "No memory for prompt\n");
 
                return;
@@ -844,23 +1172,22 @@ do_prompt(argc, argv)
 
 
 void
-do_unalias(argc, argv)
-       int             argc;
-       const char **   argv;
+do_unalias(int argc, const char ** argv)
 {
-       ALIAS * alias;
+       Alias * alias;
 
-       while (--argc > 0) {
-               alias = findalias(*++argv);
+       while (--argc > 0)
+       {
+               alias = findAlias(*++argv);
 
                if (alias == NULL)
                        continue;
 
                free(alias->name);
                free(alias->value);
-               aliascount--;
-               alias->name = aliastable[aliascount].name;
-               alias->value = aliastable[aliascount].value;    
+               aliasCount--;
+               alias->name = aliasTable[aliasCount].name;
+               alias->value = aliasTable[aliasCount].value;    
        }
 }
 
@@ -869,7 +1196,7 @@ do_unalias(argc, argv)
  * Display the prompt string.
  */
 static void
-showprompt()
+showPrompt(void)
 {
        const char *    cp;
 
@@ -883,27 +1210,25 @@ showprompt()
 
 
 static void
-catchint(val)
-       int     val;
+catchInt(int val)
 {
-       signal(SIGINT, catchint);
+       signal(SIGINT, catchInt);
 
-       intflag = TRUE;
+       intFlag = TRUE;
 
-       if (intcrlf)
+       if (intCrlf)
                write(STDOUT, "\n", 1);
 }
 
 
 static void
-catchquit(val)
-       int     val;
+catchQuit(int val)
 {
-       signal(SIGQUIT, catchquit);
+       signal(SIGQUIT, catchQuit);
 
-       intflag = TRUE;
+       intFlag = TRUE;
 
-       if (intcrlf)
+       if (intCrlf)
                write(STDOUT, "\n", 1);
 }
 
@@ -912,11 +1237,11 @@ catchquit(val)
  * Print the usage information and quit.
  */
 static void
-usage()
+usage(void)
 {
        fprintf(stderr, "Stand-alone shell (version %s)\n", version);
        fprintf(stderr, "\n");
-       fprintf(stderr, "Usage: sash [-q] [-c command] [-p prompt]\n");
+       fprintf(stderr, "Usage: sash [-a] [-q] [-c command] [-p prompt]\n");
 
        exit(1);
 }
diff --git a/sash.h b/sash.h
index ef555395cc2b4351ef01d398283fdf06b0bcd113..93fe99abd63d35378704eaec429fcc0bdacc23e4 100644 (file)
--- a/sash.h
+++ b/sash.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
 #include <ctype.h>
 
 
-#define        PATHLEN         1024
-#define        CMDLEN          10240
-#define        MAXARGS         1000
-#define        ALIASALLOC      20
+#define        PATH_LEN        1024
+#define        CMD_LEN         10240
+#define        ALIAS_ALLOC     20
+#define        EXPAND_ALLOC    1024
 #define        STDIN           0
 #define        STDOUT          1
-#define        MAXSOURCE       10
-#define        BUFSIZE         8192
+#define        MAX_SOURCE      10
+#define        BUF_SIZE        8192
 
 
-#ifndef        isblank
-#define        isblank(ch)     (((ch) == ' ') || ((ch) == '\t'))
-#endif
-
-#define        isquote(ch)     (((ch) == '"') || ((ch) == '\''))
-#define        isdecimal(ch)   (((ch) >= '0') && ((ch) <= '9'))
-#define        isoctal(ch)     (((ch) >= '0') && ((ch) <= '7'))
+#define        isBlank(ch)     (((ch) == ' ') || ((ch) == '\t'))
+#define        isDecimal(ch)   (((ch) >= '0') && ((ch) <= '9'))
+#define        isOctal(ch)     (((ch) >= '0') && ((ch) <= '7'))
+#define        isWildCard(ch)  (((ch) == '*') || ((ch) == '?') || ((ch) == '['))
 
+#ifndef MAX
+#define MAX(x, y)      ((x) > (y) ? (x) : (y))
+#endif
+#ifndef MIN
+#define MIN(x, y)      ((x) < (y) ? (x) : (y))
+#endif
 
 typedef        int     BOOL;
 
@@ -47,94 +50,97 @@ typedef     int     BOOL;
 
 
 /*
- * Macro to use function prototypes if this is an ANSI compiler.
+ * Built-in command functions.
  */
-#ifdef __STDC__
-#define        PROTO(a)        a
-#else
-#define        const
-#define        PROTO(a)        ()
+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);
+
+#ifdef HAVE_GZIP
+extern void    do_gzip(int argc, const char ** argv);
+extern void    do_gunzip(int argc, const char ** argv);
 #endif
 
-
-/*
- * Built-in command functions.
- */
-extern void    do_alias PROTO((int argc, const char ** argv));
-extern void    do_cd PROTO((int argc, const char ** argv));
-extern void    do_exec PROTO((int argc, const char ** argv));
-extern void    do_exit PROTO((int argc, const char ** argv));
-extern void    do_prompt PROTO((int argc, const char ** argv));
-extern void    do_source PROTO((int argc, const char ** argv));
-extern void    do_umask PROTO((int argc, const char ** argv));
-extern void    do_unalias PROTO((int argc, const char ** argv));
-extern void    do_help PROTO((int argc, const char ** argv));
-extern void    do_ln PROTO((int argc, const char ** argv));
-extern void    do_cp PROTO((int argc, const char ** argv));
-extern void    do_mv PROTO((int argc, const char ** argv));
-extern void    do_rm PROTO((int argc, const char ** argv));
-extern void    do_chmod PROTO((int argc, const char ** argv));
-extern void    do_mkdir PROTO((int argc, const char ** argv));
-extern void    do_rmdir PROTO((int argc, const char ** argv));
-extern void    do_mknod PROTO((int argc, const char ** argv));
-extern void    do_chown PROTO((int argc, const char ** argv));
-extern void    do_chgrp PROTO((int argc, const char ** argv));
-extern void    do_sync PROTO((int argc, const char ** argv));
-extern void    do_printenv PROTO((int argc, const char ** argv));
-extern void    do_more PROTO((int argc, const char ** argv));
-extern void    do_cmp PROTO((int argc, const char ** argv));
-extern void    do_touch PROTO((int argc, const char ** argv));
-extern void    do_ls PROTO((int argc, const char ** argv));
-extern void    do_dd PROTO((int argc, const char ** argv));
-extern void    do_tar PROTO((int argc, const char ** argv));
-extern void    do_mount PROTO((int argc, const char ** argv));
-extern void    do_umount PROTO((int argc, const char ** argv));
-extern void    do_setenv PROTO((int argc, const char ** argv));
-extern void    do_pwd PROTO((int argc, const char ** argv));
-extern void    do_echo PROTO((int argc, const char ** argv));
-extern void    do_kill PROTO((int argc, const char ** argv));
-extern void    do_grep PROTO((int argc, const char ** argv));
-extern void    do_ed PROTO((int argc, const char ** argv));
-extern void    do_gzip PROTO((int argc, const char ** argv));
-extern void    do_gunzip PROTO((int argc, const char ** argv));
-extern void    do_where PROTO((int argc, const char ** argv));
+#ifdef HAVE_EXT2
+extern void    do_lsattr(int argc, const char ** argv);
+extern void    do_chattr(int argc, const char ** argv);
+#endif
 
 
 /*
  * Global utility routines.
  */
-extern const char *    modestring PROTO((int mode));
-extern const char *    timestring PROTO((time_t timeval));
-extern BOOL            isadir PROTO((const char * name));
-extern int             namesort PROTO((const void * p1, const void * p2));
-extern char *          getchunk PROTO((int size));
-extern char *          chunkstrdup PROTO((const char *));
-extern void            freechunks PROTO((void));
-extern int             fullWrite PROTO((int fd, const char * buf, int len));
-extern BOOL            match PROTO((const char * text, const char * pattern));
-
-extern const char *    buildname
-       PROTO((const char * dirname, const char * filename));
+extern const char *    modeString(int mode);
+extern const char *    timeString(time_t timeVal);
+extern BOOL            isDirectory(const char * name);
+extern BOOL            isDevice(const char * name);
+extern int             nameSort(const void * p1, const void * p2);
+extern char *          getChunk(int size);
+extern char *          chunkstrdup(const char *);
+extern void            freeChunks(void);
+extern int             fullWrite(int fd, const char * buf, int len);
+extern int             fullRead(int fd, char * buf, int len);
+extern BOOL            match(const char * text, const char * pattern);
 
-extern BOOL    makeargs
-       PROTO((const char * cmd, int * argcptr, const char *** argvptr));
+extern const char *    buildName
+       (const char * dirName, const char * fileName);
 
-extern BOOL    copyfile
-       PROTO((const char * srcname, const char * destname, BOOL setmodes));
+extern BOOL    makeArgs
+       (const char * cmd, int * argcPtr, const char *** argvPtr);
 
-extern BOOL    makestring
-       PROTO((int argc, const char ** argv, char * buf, int buflen));
+extern BOOL    copyFile
+       (const char * srcName, const char * destName, BOOL setModes);
 
-extern int     expandwildcards
-       PROTO((const char *name, int maxargc, const char * retargv[]));
+extern BOOL    makeString
+       (int argc, const char ** argv, char * buf, int bufLen);
 
+extern int     expandWildCards
+       (const char * fileNamePattern, const char *** retFileTable);
 
 
 /*
  * Global variable to indicate that an SIGINT occurred.
  * This is used to stop processing.
  */
-extern BOOL    intflag;
+extern BOOL    intFlag;
 
 #endif
 
diff --git a/utils.c b/utils.c
index faef57149873d076c981e81644c0f91e7136eb12..d1281229f394aca9c87f7ebb31ff575260d62a91 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998 by David I. Bell
+ * Copyright (c) 1999 by David I. Bell
  * Permission is granted to use, distribute, or modify this source,
  * provided that this copyright notice remains intact.
  *
 #include <utime.h>
 
 
+/*
+ * A chunk of data.
+ * Chunks contain data which is allocated as needed, but which is
+ * not freed until all of the data needs freeing, such as at
+ * the beginning of the next command.
+ */
 typedef        struct  chunk   CHUNK;
-#define        CHUNKINITSIZE   4
+#define        CHUNK_INIT_SIZE 4
 
-struct chunk   {
+struct chunk
+{
        CHUNK * next;
-       char    data[CHUNKINITSIZE];    /* actually of varying length */
+       char    data[CHUNK_INIT_SIZE];  /* actually of varying length */
 };
 
 
-static CHUNK * chunklist;
+static CHUNK * chunkList;
 
 
 
@@ -32,8 +39,7 @@ static        CHUNK * chunklist;
  * This is static and so is overwritten on each call.
  */
 const char *
-modestring(mode)
-       int     mode;
+modeString(int mode)
 {
        static  char    buf[12];
 
@@ -96,14 +102,13 @@ modestring(mode)
 
 
 /*
- * Get the time to be used for a file.
+ * Get the time string to be used for a file.
  * This is down to the minute for new files, but only the date for old files.
  * The string is returned from a static buffer, and so is overwritten for
  * each call.
  */
 const char *
-timestring(timeval)
-       time_t  timeval;
+timeString(time_t timeVal)
 {
        time_t          now;
        char *          str;
@@ -111,12 +116,13 @@ timestring(timeval)
 
        time(&now);
 
-       str = ctime(&timeval);
+       str = ctime(&timeVal);
 
        strcpy(buf, &str[4]);
        buf[12] = '\0';
 
-       if ((timeval > now) || (timeval < now - 365*24*60*60L)) {
+       if ((timeVal > now) || (timeVal < now - 365*24*60*60L))
+       {
                strcpy(&buf[7], &str[20]);
                buf[11] = '\0';
        }
@@ -126,19 +132,34 @@ timestring(timeval)
 
 
 /*
- * Return TRUE if a filename is a directory.
+ * Return TRUE if a fileName is a directory.
+ * Nonexistant files return FALSE.
+ */
+BOOL
+isDirectory(const char * name)
+{
+       struct  stat    statBuf;
+
+       if (stat(name, &statBuf) < 0)
+               return FALSE;
+
+       return S_ISDIR(statBuf.st_mode);
+}
+
+
+/*
+ * Return TRUE if a filename is a block or character device.
  * Nonexistant files return FALSE.
  */
 BOOL
-isadir(name)
-       const char *    name;
+isDevice(const char * name)
 {
-       struct  stat    statbuf;
+       struct  stat    statBuf;
 
-       if (stat(name, &statbuf) < 0)
+       if (stat(name, &statBuf) < 0)
                return FALSE;
 
-       return S_ISDIR(statbuf.st_mode);
+       return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode);
 }
 
 
@@ -149,57 +170,64 @@ isadir(name)
  * be set.)
  */
 BOOL
-copyfile(srcname, destname, setmodes)
-       const char *    srcname;
-       const char *    destname;
-       BOOL            setmodes;
+copyFile(
+       const char *    srcName,
+       const char *    destName,
+       BOOL            setModes
+)
 {
        int             rfd;
        int             wfd;
        int             rcc;
-       char            buf[BUFSIZE];
-       struct  stat    statbuf1;
-       struct  stat    statbuf2;
+       char            buf[BUF_SIZE];
+       struct  stat    statBuf1;
+       struct  stat    statBuf2;
        struct  utimbuf times;
        
-       if (stat(srcname, &statbuf1) < 0) {
-               perror(srcname);
+       if (stat(srcName, &statBuf1) < 0)
+       {
+               perror(srcName);
 
                return FALSE;
        }
 
-       if (stat(destname, &statbuf2) < 0) {
-               statbuf2.st_ino = -1;
-               statbuf2.st_dev = -1;
+       if (stat(destName, &statBuf2) < 0)
+       {
+               statBuf2.st_ino = -1;
+               statBuf2.st_dev = -1;
        }
 
-       if ((statbuf1.st_dev == statbuf2.st_dev) &&
-               (statbuf1.st_ino == statbuf2.st_ino))
+       if ((statBuf1.st_dev == statBuf2.st_dev) &&
+               (statBuf1.st_ino == statBuf2.st_ino))
        {
-               fprintf(stderr, "Copying file \"%s\" to itself\n", srcname);
+               fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
 
                return FALSE;
        }
 
-       rfd = open(srcname, O_RDONLY);
+       rfd = open(srcName, O_RDONLY);
 
-       if (rfd < 0) {
-               perror(srcname);
+       if (rfd < 0)
+       {
+               perror(srcName);
 
                return FALSE;
        }
 
-       wfd = creat(destname, statbuf1.st_mode);
+       wfd = creat(destName, statBuf1.st_mode);
 
-       if (wfd < 0) {
-               perror(destname);
+       if (wfd < 0)
+       {
+               perror(destName);
                close(rfd);
 
                return FALSE;
        }
 
-       while ((rcc = read(rfd, buf, sizeof(buf))) > 0) {
-               if (intflag) {
+       while ((rcc = read(rfd, buf, sizeof(buf))) > 0)
+       {
+               if (intFlag)
+               {
                        close(rfd);
                        close(wfd);
 
@@ -210,28 +238,31 @@ copyfile(srcname, destname, setmodes)
                        goto error_exit;
        }
 
-       if (rcc < 0) {
-               perror(srcname);
+       if (rcc < 0)
+       {
+               perror(srcName);
                goto error_exit;
        }
 
        (void) close(rfd);
 
-       if (close(wfd) < 0) {
-               perror(destname);
+       if (close(wfd) < 0)
+       {
+               perror(destName);
 
                return FALSE;
        }
 
-       if (setmodes) {
-               (void) chmod(destname, statbuf1.st_mode);
+       if (setModes)
+       {
+               (void) chmod(destName, statBuf1.st_mode);
 
-               (void) chown(destname, statbuf1.st_uid, statbuf1.st_gid);
+               (void) chown(destName, statBuf1.st_uid, statBuf1.st_gid);
 
-               times.actime = statbuf1.st_atime;
-               times.modtime = statbuf1.st_mtime;
+               times.actime = statBuf1.st_atime;
+               times.modtime = statBuf1.st_mtime;
 
-               (void) utime(destname, &times);
+               (void) utime(destName, &times);
        }
 
        return TRUE;
@@ -247,47 +278,43 @@ error_exit:
 
 /*
  * Build a path name from the specified directory name and file name.
- * If the directory name is NULL, then the original filename is returned.
+ * If the directory name is NULL, then the original fileName is returned.
  * The built path is in a static area, and is overwritten for each call.
  */
 const char *
-buildname(dirname, filename)
-       const char *    dirname;
-       const char *    filename;
+buildName(const char * dirName, const char * fileName)
 {
        const char *    cp;
-       static  char    buf[PATHLEN];
+       static  char    buf[PATH_LEN];
 
-       if ((dirname == NULL) || (*dirname == '\0'))
-               return filename;
+       if ((dirName == NULL) || (*dirName == '\0'))
+               return fileName;
 
-       cp = strrchr(filename, '/');
+       cp = strrchr(fileName, '/');
 
        if (cp)
-               filename = cp + 1;
+               fileName = cp + 1;
 
-       strcpy(buf, dirname);
+       strcpy(buf, dirName);
        strcat(buf, "/");
-       strcat(buf, filename);
+       strcat(buf, fileName);
 
        return buf;
 }
 
 
 /*
- * Expand the wildcards in a filename, if any.
- * Returns an argument list with matching filenames in sorted order.
+ * Expand the wildcards in a fileName wildcard pattern, if any.
+ * Returns an argument list with matching fileNames in sorted order.
  * The expanded names are stored in memory chunks which can later all
- * be freed at once.  Returns zero if the name is not a wildcard, or
- * returns the count of matched files if the name is a wildcard and
- * there was at least one match, or returns -1 if either no filenames
- * matched or too many filenames matched (with an error output).
+ * be freed at once.  The returned list is only valid until the next
+ * call or until the next command.  Returns zero if the name is not a
+ * wildcard, or returns the count of matched files if the name is a
+ * wildcard and there was at least one match, or returns -1 if either
+ * no fileNames matched or there was an allocation error.
  */
 int
-expandwildcards(name, maxargc, retargv)
-       const char *    name;
-       int             maxargc;
-       const char *    retargv[];
+expandWildCards(const char * fileNamePattern, const char *** retFileTable)
 {
        const char *    last;
        const char *    cp1;
@@ -296,120 +323,214 @@ expandwildcards(name, maxargc, retargv)
        char *          str;
        DIR *           dirp;
        struct dirent * dp;
-       int             dirlen;
-       int             matches;
-       char            dirname[PATHLEN];
+       int             dirLen;
+       int             newFileTableSize;
+       char **         newFileTable;
+       char            dirName[PATH_LEN];
 
-       last = strrchr(name, '/');
+       static int      fileCount;
+       static int      fileTableSize;
+       static char **  fileTable;
 
-       if (last)
-               last++;
-       else
-               last = name;
+       /*
+        * Clear the return values until we know their final values.
+        */
+       fileCount = 0;
+       *retFileTable = NULL;
 
-       cp1 = strchr(name, '*');
-       cp2 = strchr(name, '?');
-       cp3 = strchr(name, '[');
+       /*
+        * Scan the file name pattern for any wildcard characters.
+        */
+       cp1 = strchr(fileNamePattern, '*');
+       cp2 = strchr(fileNamePattern, '?');
+       cp3 = strchr(fileNamePattern, '[');
 
+       /*
+        * If there are no wildcard characters then return zero to
+        * indicate that there was actually no wildcard pattern.
+        */
        if ((cp1 == NULL) && (cp2 == NULL) && (cp3 == NULL))
                return 0;
 
+       /*
+        * There are wildcards in the specified filename.
+        * Get the last component of the file name.
+        */
+       last = strrchr(fileNamePattern, '/');
+
+       if (last)
+               last++;
+       else
+               last = fileNamePattern;
+
+       /*
+        * If any wildcards were found before the last filename component
+        * then return an error.
+        */
        if ((cp1 && (cp1 < last)) || (cp2 && (cp2 < last)) ||
                (cp3 && (cp3 < last)))
        {
-               fprintf(stderr, "Wildcards only implemented for last filename component\n");
+               fprintf(stderr,
+               "Wildcards only implemented for last file name component\n");
 
                return -1;
        }
 
-       dirname[0] = '.';
-       dirname[1] = '\0';
+       /*
+        * Assume at first that we are scanning the current directory.
+        */
+       dirName[0] = '.';
+       dirName[1] = '\0';
 
-       if (last != name) {
-               memcpy(dirname, name, last - name);
-               dirname[last - name - 1] = '\0';
+       /*
+        * If there was a directory given as part of the file name then
+        * copy it and null terminate it.
+        */
+       if (last != fileNamePattern)
+       {
+               memcpy(dirName, fileNamePattern, last - fileNamePattern);
+               dirName[last - fileNamePattern - 1] = '\0';
 
-               if (dirname[0] == '\0') {
-                       dirname[0] = '/';
-                       dirname[1] = '\0';
+               if (dirName[0] == '\0')
+               {
+                       dirName[0] = '/';
+                       dirName[1] = '\0';
                }
        }
 
-       dirp = opendir(dirname);
+       /*
+        * Open the directory containing the files to be checked.
+        */
+       dirp = opendir(dirName);
 
-       if (dirp == NULL) {
-               perror(dirname);
+       if (dirp == NULL)
+       {
+               perror(dirName);
 
                return -1;
        }
 
-       dirlen = strlen(dirname);
+       /*
+        * Prepare the directory name for use in making full path names.
+        */
+       dirLen = strlen(dirName);
 
-       if (last == name) {
-               dirlen = 0;
-               dirname[0] = '\0';
-       } else if (dirname[dirlen - 1] != '/') {
-               dirname[dirlen++] = '/';
-               dirname[dirlen] = '\0';
+       if (last == fileNamePattern)
+       {
+               dirLen = 0;
+               dirName[0] = '\0';
+       }
+       else if (dirName[dirLen - 1] != '/')
+       {
+               dirName[dirLen++] = '/';
+               dirName[dirLen] = '\0';
        }
 
-       matches = 0;
-
-       while ((dp = readdir(dirp)) != NULL) {
+       /*
+        * Find all of the files in the directory and check them against
+        * the wildcard pattern.
+        */
+       while ((dp = readdir(dirp)) != NULL)
+       {
+               /*
+                * Skip the current and parent directories.
+                */
                if ((strcmp(dp->d_name, ".") == 0) ||
                        (strcmp(dp->d_name, "..") == 0))
                {
                        continue;
                }
 
+               /*
+                * If the file name doesn't match the pattern then skip it.
+                */
                if (!match(dp->d_name, last))
                        continue;
 
-               if (matches >= maxargc) {
-                       fprintf(stderr, "Too many filename matches\n");
-                       closedir(dirp);
+               /*
+                * This file name is selected.
+                * See if we need to reallocate the file name table.
+                */
+               if (fileCount >= fileTableSize)
+               {
+                       /*
+                        * Increment the file table size and reallocate it.
+                        */
+                       newFileTableSize = fileTableSize + EXPAND_ALLOC;
 
-                       return -1;
+                       newFileTable = (char **) realloc((char *) fileTable,
+                               (newFileTableSize * sizeof(char *)));
+
+                       if (newFileTable == NULL)
+                       {
+                               fprintf(stderr, "Cannot allocate file list\n");
+                               closedir(dirp);
+
+                               return -1;
+                       }
+
+                       fileTable = newFileTable;
+                       fileTableSize = newFileTableSize;
                }
 
-               str = getchunk(dirlen + strlen(dp->d_name) + 1);
+               /*
+                * Allocate space for storing the file name in a chunk.
+                */
+               str = getChunk(dirLen + strlen(dp->d_name) + 1);
 
-               if (str == NULL) {
-                       fprintf(stderr, "No memory for filename\n");
+               if (str == NULL)
+               {
+                       fprintf(stderr, "No memory for file name\n");
                        closedir(dirp);
 
                        return -1;
                }
 
-               if (dirlen)
-                       memcpy(str, dirname, dirlen);
+               /*
+                * Save the file name in the chunk.
+                */
+               if (dirLen)
+                       memcpy(str, dirName, dirLen);
 
-               strcpy(str + dirlen, dp->d_name);
+               strcpy(str + dirLen, dp->d_name);
 
-               retargv[matches++] = str;
+               /*
+                * Save the allocated file name into the file table.
+                */
+               fileTable[fileCount++] = str;
        }
 
+       /*
+        * Close the directory and check for any matches.
+        */
        closedir(dirp);
 
-       if (matches == 0) {
+       if (fileCount == 0)
+       {
                fprintf(stderr, "No matches\n");
 
                return -1;
        }
 
-       qsort((void *) retargv, matches, sizeof(char *), namesort);
+       /*
+        * Sort the list of file names.
+        */
+       qsort((void *) fileTable, fileCount, sizeof(char *), nameSort);
+
+       /*
+        * Return the file list and count.
+        */
+       *retFileTable = (const char **) fileTable;
 
-       return matches;
+       return fileCount;
 }
 
 
 /*
- * Sort routine for list of filenames.
+ * Sort routine for list of fileNames.
  */
 int
-namesort(p1, p2)
-       const void *    p1;
-       const void *    p2;
+nameSort(const void * p1, const void * p2)
 {
        const char **   s1;
        const char **   s2;
@@ -432,31 +553,32 @@ namesort(p1, p2)
  *  Adapted from code written by Ingo Wilken.
  */
 BOOL
-match(text, pattern)
-       const char *    text;
-       const char *    pattern;
+match(const char * text, const char * pattern)
 {
-       const char *    retrypat;
-       const char *    retrytxt;
+       const char *    retryPat;
+       const char *    retryText;
        int             ch;
        BOOL            found;
 
-       retrypat = NULL;
-       retrytxt = NULL;
+       retryPat = NULL;
+       retryText = NULL;
 
-       while (*text || *pattern) {
+       while (*text || *pattern)
+       {
                ch = *pattern++;
 
-               switch (ch) {
+               switch (ch)
+               {
                        case '*':  
-                               retrypat = pattern;
-                               retrytxt = text;
+                               retryPat = pattern;
+                               retryText = text;
                                break;
 
                        case '[':  
                                found = FALSE;
 
-                               while ((ch = *pattern++) != ']') {
+                               while ((ch = *pattern++) != ']')
+                               {
                                        if (ch == '\\')
                                                ch = *pattern++;
 
@@ -467,9 +589,10 @@ match(text, pattern)
                                                found = TRUE;
                                }
 
-                               if (!found) {
-                                       pattern = retrypat;
-                                       text = ++retrytxt;
+                               if (!found)
+                               {
+                                       pattern = retryPat;
+                                       text = ++retryText;
                                }
 
                                /* fall into next case */
@@ -489,15 +612,17 @@ match(text, pattern)
                                /* fall into next case */
 
                        default:        
-                               if (*text == ch) {
+                               if (*text == ch)
+                               {
                                        if (*text)
                                                text++;
                                        break;
                                }
 
-                               if (*text) {
-                                       pattern = retrypat;
-                                       text = ++retrytxt;
+                               if (*text)
+                               {
+                                       pattern = retryPat;
+                                       text = ++retryText;
                                        break;
                                }
 
@@ -513,51 +638,283 @@ match(text, pattern)
 
 
 /*
- * Take a command string, and break it up into an argc, argv list.
- * The returned argument list and strings are in static memory, and so
- * are overwritten on each call.  The argument array is ended with an
- * extra NULL pointer for convenience.  Returns TRUE if successful,
- * or FALSE on an error with a message already output.
+ * Take a command string and break it up into an argc, argv list while
+ * handling quoting and wildcards.  The returned argument list and
+ * strings are in static memory, and so are overwritten on each call.
+ * The argument list is ended with a NULL pointer for convenience.
+ * Returns TRUE if successful, or FALSE on an error with a message
+ * already output.
  */
 BOOL
-makeargs(cmd, argcptr, argvptr)
-       const char *    cmd;
-       int *           argcptr;
-       const char ***  argvptr;
+makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
 {
+       const char *            argument;
        char *                  cp;
-       int                     argc;
-       static char             strings[CMDLEN+1];
-       static const char *     argtable[MAXARGS+1];
+       char *                  cpOut;
+       char *                  newStrings;
+       const char **           fileTable;
+       const char **           newArgTable;
+       int                     newArgTableSize;
+       int                     fileCount;
+       int                     len;
+       int                     ch;
+       int                     quote;
+       BOOL                    quotedWildCards;
+       BOOL                    unquotedWildCards;
+
+       static int              stringsLength;
+       static char *           strings;
+       static int              argCount;
+       static int              argTableSize;
+       static const char **    argTable;
 
        /*
-        * Copy the command string and then break it apart
-        * into separate arguments.
+        * Clear the returned values until we know them.
         */
-       strcpy(strings, cmd);
-       argc = 0;
-       cp = strings;
+       argCount = 0;
+       *retArgc = 0;
+       *retArgv = NULL;
+
+       /*
+        * Copy the command string into a buffer that we can modify,
+        * reallocating it if necessary.
+        */
+       len = strlen(cmd) + 1;
 
-       while (*cp) {
-               if (argc >= MAXARGS) {
-                       fprintf(stderr, "Too many arguments\n");
+       if (len > stringsLength)
+       {
+               newStrings = realloc(strings, len);
+
+               if (newStrings == NULL)
+               {
+                       fprintf(stderr, "Cannot allocate string\n");
 
                        return FALSE;
                }
 
-               argtable[argc++] = (const char *) cp;
+               strings = newStrings;
+               stringsLength = len;
+       }
+
+       memcpy(strings, cmd, len);
+       cp = strings;
+
+       /*
+        * Keep parsing the command string as long as there are any
+        * arguments left.
+        */
+       while (*cp)
+       {
+               /*
+                * Save the beginning of this argument.
+                */
+               argument = cp;
+               cpOut = cp;
+
+               /*
+                * Reset quoting and wildcarding for this argument.
+                */
+               quote = '\0';
+               quotedWildCards = FALSE;
+               unquotedWildCards = FALSE;
+
+               /*
+                * Loop over the string collecting the next argument while
+                * looking for quoted strings or quoted characters, and
+                * remembering whether there are any wildcard characters
+                * in the argument.
+                */
+               while (*cp)
+               {
+                       ch = *cp++;
+
+                       /*
+                        * If we are not in a quote and we see a blank then
+                        * this argument is done.
+                        */
+                       if (isBlank(ch) && (quote == '\0'))
+                               break;
+
+                       /*
+                        * If we see a backslash then accept the next
+                        * character no matter what it is.
+                        */
+                       if (ch == '\\')
+                       {
+                               ch = *cp++;
+
+                               /*
+                                * Make sure there is a next character.
+                                */
+                               if (ch == '\0')
+                               {
+                                       fprintf(stderr,
+                                               "Bad quoted character\n");
+
+                                       return FALSE;
+                               }
+
+                               /*
+                                * Remember whether the quoted character
+                                * is a wildcard.
+                                */
+                               if (isWildCard(ch))
+                                       quotedWildCards = TRUE;
+
+                               *cpOut++ = ch;
+
+                               continue;
+                       }
+
+                       /*
+                        * If we see one of the wildcard characters then
+                        * remember whether it was seen inside or outside
+                        * of quotes.
+                        */
+                       if (isWildCard(ch))
+                       {
+                               if (quote)
+                                       quotedWildCards = TRUE;
+                               else
+                                       unquotedWildCards = TRUE;
+                       }
+
+                       /*
+                        * If we were in a quote and we saw the same quote
+                        * character again then the quote is done.
+                        */
+                       if (ch == quote)
+                       {
+                               quote = '\0';
+
+                               continue;
+                       }
+
+                       /*
+                        * If we weren't in a quote and we see either type
+                        * of quote character, then remember that we are
+                        * now inside of a quote.
+                        */
+                       if ((quote == '\0') && ((ch == '\'') || (ch == '"')))
+                       {
+                               quote = ch;
+
+                               continue;
+                       }
+
+                       /*
+                        * Store the character.
+                        */
+                       *cpOut++ = ch;
+               }
+
+               /*
+                * Make sure that quoting is terminated properly.
+                */
+               if (quote)
+               {
+                       fprintf(stderr, "Unmatched quote character\n");
+
+                       return FALSE;
+               }
 
-               while (*cp && !isblank(*cp))
-                       cp++;
+               /*
+                * Null terminate the argument if it had shrunk, and then
+                * skip over all blanks to the next argument, nulling them
+                * out too.
+                */
+               if (cp != cpOut)
+                       *cpOut = '\0';
 
-               while (isblank(*cp))
+               while (isBlank(*cp))
                        *cp++ = '\0';
+
+               /*
+                * If both quoted and unquoted wildcards were used then
+                * complain since we don't handle them properly.
+                */
+               if (quotedWildCards && unquotedWildCards)
+               {
+                       fprintf(stderr,
+                               "Cannot use quoted and unquoted wildcards\n");
+
+                       return FALSE;
+               }
+
+               /*
+                * Expand the argument into the matching filenames or accept
+                * it as is depending on whether there were any unquoted
+                * wildcard characters in it.
+                */
+               if (unquotedWildCards)
+               {
+                       /*
+                        * Expand the argument into the matching filenames.
+                        */
+                       fileCount = expandWildCards(argument, &fileTable);
+
+                       /*
+                        * Return an error if the wildcards failed to match.
+                        */
+                       if (fileCount < 0)
+                               return FALSE;
+
+                       if (fileCount == 0)
+                       {
+                               fprintf(stderr, "Wildcard expansion error\n");
+
+                               return FALSE;
+                       }
+               }
+               else
+               {
+                       /*
+                        * Set up to only store the argument itself.
+                        */
+                       fileTable = &argument;
+                       fileCount = 1;
+               }
+
+               /*
+                * Now reallocate the argument table to hold the file name.
+                */
+               if (argCount + fileCount >= argTableSize)
+               {
+                       newArgTableSize = argCount + fileCount + 1;
+
+                       newArgTable = (const char **) realloc(argTable,
+                               (sizeof(const char *) * newArgTableSize));
+
+                       if (newArgTable == NULL)
+                       {
+                               fprintf(stderr, "No memory for arg list\n");
+
+                               return FALSE;
+                       }
+
+                       argTable = newArgTable;
+                       argTableSize = newArgTableSize;
+               }
+
+               /*
+                * Copy the new arguments to the end of the old ones.
+                */
+               memcpy((void *) &argTable[argCount], (const void *) fileTable,
+                       (sizeof(const char **) * fileCount));
+
+               /*
+                * Add to the argument count.
+                */
+               argCount += fileCount;
        }
 
-       argtable[argc] = NULL;
+       /*
+        * Null terminate the argument list and return it.
+        */
+       argTable[argCount] = NULL;
 
-       *argcptr = argc;
-       *argvptr = argtable;
+       *retArgc = argCount;
+       *retArgv = argTable;
 
        return TRUE;
 }
@@ -570,18 +927,21 @@ makeargs(cmd, argcptr, argvptr)
  * arguments correctly.
  */
 BOOL
-makestring(argc, argv, buf, buflen)
-       int             argc;
-       const char **   argv;
-       char *          buf;
-       int             buflen;
+makeString(
+       int             argc,
+       const char **   argv,
+       char *          buf,
+       int             bufLen
+)
 {
        int     len;
 
-       while (argc-- > 0) {
+       while (argc-- > 0)
+       {
                len = strlen(*argv);
 
-               if (len >= buflen) {
+               if (len >= bufLen)
+               {
                        fprintf(stderr, "Argument string too long\n");
 
                        return FALSE;
@@ -590,12 +950,12 @@ makestring(argc, argv, buf, buflen)
                strcpy(buf, *argv++);
 
                buf += len;
-               buflen -= len;
+               bufLen -= len;
 
                if (argc)
                        *buf++ = ' ';
 
-               buflen--; 
+               bufLen--; 
        }
 
        *buf = '\0';
@@ -611,21 +971,20 @@ makestring(argc, argv, buf, buflen)
  * an individual chunk.
  */
 char *
-getchunk(size)
-       int     size;
+getChunk(int size)
 {
        CHUNK * chunk;
 
-       if (size < CHUNKINITSIZE)
-               size = CHUNKINITSIZE;
+       if (size < CHUNK_INIT_SIZE)
+               size = CHUNK_INIT_SIZE;
 
-       chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNKINITSIZE);
+       chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE);
 
        if (chunk == NULL)
                return NULL;
 
-       chunk->next = chunklist;
-       chunklist = chunk;
+       chunk->next = chunkList;
+       chunkList = chunk;
 
        return chunk->data;
 }
@@ -634,22 +993,21 @@ getchunk(size)
 /*
  * Duplicate a string value using the chunk allocator.
  * The returned string cannot be individually freed, but can only be freed
- * with other strings when freechunks is called.  Returns NULL on failure.
+ * with other strings when freeChunks is called.  Returns NULL on failure.
  */
 char *
-chunkstrdup(str)
-       const char *    str;
+chunkstrdup(const char * str)
 {
        int     len;
-       char *  newstr;
+       char *  newStr;
 
        len = strlen(str) + 1;
-       newstr = getchunk(len);
+       newStr = getChunk(len);
 
-       if (newstr)
-               memcpy(newstr, str, len);
+       if (newStr)
+               memcpy(newStr, str, len);
 
-       return newstr;
+       return newStr;
 }
 
 
@@ -658,13 +1016,14 @@ chunkstrdup(str)
  * call to this routine.
  */
 void
-freechunks()
+freeChunks(void)
 {
        CHUNK * chunk;
 
-       while (chunklist) {
-               chunk = chunklist;
-               chunklist = chunk->next;
+       while (chunkList)
+       {
+               chunk = chunkList;
+               chunkList = chunk->next;
                free((char *) chunk);
        }
 }
@@ -676,17 +1035,15 @@ freechunks()
  * Returns the amount written, or -1 on an error.
  */
 int
-fullWrite(fd, buf, len)
-       int             fd;
-       const char *    buf;
-       int             len;
+fullWrite(int fd, const char * buf, int len)
 {
        int     cc;
        int     total;
 
        total = 0;
 
-       while (len > 0) {
+       while (len > 0)
+       {
                cc = write(fd, buf, len);
 
                if (cc < 0)
@@ -700,4 +1057,37 @@ fullWrite(fd, buf, len)
        return total;
 }
 
+
+/*
+ * Read all of the supplied buffer from a file.
+ * This does multiple reads as necessary.
+ * Returns the amount read, or -1 on an error.
+ * A short read is returned on an end of file.
+ */
+int
+fullRead(int fd, char * buf, int len)
+{
+       int     cc;
+       int     total;
+
+       total = 0;
+
+       while (len > 0)
+       {
+               cc = read(fd, buf, len);
+
+               if (cc < 0)
+                       return -1;
+
+               if (cc == 0)
+                       break;
+
+               buf += cc;
+               total+= cc;
+               len -= cc;
+       }
+
+       return total;
+}
+
 /* END CODE */