aboutsummaryrefslogtreecommitdiffstats
path: root/src/sash
diff options
context:
space:
mode:
Diffstat (limited to 'src/sash')
-rw-r--r--src/sash/CHANGES31
-rw-r--r--src/sash/Makefile69
-rw-r--r--src/sash/README17
-rw-r--r--src/sash/cmd_ar.c1019
-rw-r--r--src/sash/cmd_chattr.c265
-rw-r--r--src/sash/cmd_dd.c372
-rw-r--r--src/sash/cmd_ed.c1440
-rw-r--r--src/sash/cmd_file.c235
-rw-r--r--src/sash/cmd_find.c376
-rw-r--r--src/sash/cmd_grep.c197
-rw-r--r--src/sash/cmd_gzip.c698
-rw-r--r--src/sash/cmd_ls.c597
-rw-r--r--src/sash/cmd_tar.c1277
-rw-r--r--src/sash/cmds.c1562
-rw-r--r--src/sash/dirent.h10
-rw-r--r--src/sash/sash.1591
-rw-r--r--src/sash/sash.c1373
-rw-r--r--src/sash/sash.h169
-rw-r--r--src/sash/utils.c1144
19 files changed, 0 insertions, 11442 deletions
diff --git a/src/sash/CHANGES b/src/sash/CHANGES
deleted file mode 100644
index c87d6d5..0000000
--- a/src/sash/CHANGES
+++ /dev/null
@@ -1,31 +0,0 @@
-These are the major changes from version 3.7 to version 3.8:
-
-The Makefile has been updated for several distribution's standards.
-The ext2_fs include file location has been changed.
-Some compiler warnings were fixed.
-
-The -ls command has the -n option to print numeric user and group ids.
-
-The -n option might be needed in case the unsuppressable dynamic
-linking used to lookup the names fails.
-
-The -chroot, -pivot_root, and -losetup commands have been added.
-
-The exit status for commands has been implemented (such as -exit).
-Thanks to Tollef Fog Heen for the patches.
-
-
-These are the major changes from version 3.6 to version 3.7:
-
-A few bugs in the dd command have been fixed.
-
-
-These are the major changes from version 3.5 to version 3.6:
-
-The -mount and -umount commands have been modified to work for both
-Linux and BSD systems. Thanks to Wilbern Cobb for the patches.
-
-The -e and -s options for the -mount command have been added.
-
-The -f option was added to the command line so that script files
-using sash can be executed.
diff --git a/src/sash/Makefile b/src/sash/Makefile
deleted file mode 100644
index 02a895a..0000000
--- a/src/sash/Makefile
+++ /dev/null
@@ -1,69 +0,0 @@
-#
-# Makefile for sash
-#
-# The HAVE_GZIP definition adds the -gzip and -gunzip commands.
-# The HAVE_LINUX_ATTR definition adds the -chattr and -lsattr commands.
-# The HAVE_LINUX_CHROOT definition adds the -chroot command.
-# The HAVE_LINUX_PIVOT definition adds the -pivot_root command.
-# The HAVE_LINUX_LOSETUP definition adds the -losetup command.
-# The HAVE_LINUX_MOUNT definition makes -mount and -umount work on Linux.
-# The HAVE_BSD_MOUNT definition makes -mount and -umount work on BSD.
-# The MOUNT_TYPE definition sets the default file system type for -mount.
-#
-# Note that the linker may show warnings about 'statically linked
-# programs' requiring getpwnam, getpwuid, getgrnam and getgrgid.
-# This is unavoidable since those routines use dynamic libraries anyway.
-# Sash will still run, but if there are shared library problems then
-# the user might have to be be careful when using the -chown, -chgrp,
-# and -ls commands.
-#
-
-CC=arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb
-
-#HAVE_GZIP = 1
-#HAVE_LINUX_ATTR = 1
-#HAVE_LINUX_CHROOT = 1
-#HAVE_LINUX_LOSETUP = 1
-#HAVE_LINUX_PIVOT = 1
-#HAVE_LINUX_MOUNT = 1
-#HAVE_BSD_MOUNT = 0
-MOUNT_TYPE = '""'
-
-OPT = -O3 -ffreestanding
-
-CFLAGS = $(OPT) -Wall -Wmissing-prototypes \
- -DMOUNT_TYPE=$(MOUNT_TYPE) #\
- -DHAVE_GZIP=$(HAVE_GZIP) \
- -DHAVE_LINUX_ATTR=$(HAVE_LINUX_ATTR) \
- -DHAVE_LINUX_CHROOT=$(HAVE_LINUX_CHROOT) \
- -DHAVE_LINUX_LOSETUP=$(HAVE_LINUX_LOSETUP) \
- -DHAVE_LINUX_PIVOT=$(HAVE_LINUX_PIVOT) \
- -DHAVE_LINUX_MOUNT=$(HAVE_LINUX_MOUNT) \
- -DHAVE_BSD_MOUNT=$(HAVE_BSD_MOUNT) \
-
-#LDFLAGS = -static
-#LIBS = -lz
-
-
-DESTDIR =
-BINDIR = /bin
-MANDIR = /usr/man
-
-
-#OBJS = sash.o cmds.o cmd_dd.o cmd_ed.o cmd_grep.o cmd_ls.o cmd_tar.o \
- cmd_gzip.o cmd_find.o cmd_file.o cmd_chattr.o cmd_ar.o utils.o
-OBJS = sash.o cmds.o cmd_ls.o utils.o
-
-
-sash: $(OBJS)
- $(CC) $(LDFLAGS) -o sash $(OBJS) $(LIBS)
- arm-none-eabi-strip sash
-
-clean:
- rm -f $(OBJS) sash
-
-install: sash
- cp sash $(DESTDIR)/$(BINDIR)/sash
- cp sash.1 $(DESTDIR)/$(MANDIR)/man1/sash.1
-
-$(OBJS): sash.h
diff --git a/src/sash/README b/src/sash/README
deleted file mode 100644
index 154f1f7..0000000
--- a/src/sash/README
+++ /dev/null
@@ -1,17 +0,0 @@
-This is release 3.8 of sash, my stand-alone shell for Linux or other systems.
-
-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.
-
-Type "make install" to build and install the program and man page.
-
-Several options in the Makefile can be changed to build sash for
-other UNIX-like systems. In particular, dependencies on Linux file
-systems can be removed and the mount command can be configured.
-
-David I. Bell
-dbell@tip.net.au
-8 March 2014
diff --git a/src/sash/cmd_ar.c b/src/sash/cmd_ar.c
deleted file mode 100644
index 6c6181e..0000000
--- a/src/sash/cmd_ar.c
+++ /dev/null
@@ -1,1019 +0,0 @@
-/*
- * 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) 2014 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 */
- 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);
-
-
-int
-do_ar(int argc, const char ** argv)
-{
- const char * options;
- const char * archiveName;
- BOOL doExtract;
- BOOL doTable;
- BOOL doPrint;
- BOOL verbose;
- int r;
- Archive arch;
-
- r = 0;
- verbose = FALSE;
- doExtract = FALSE;
- doTable = FALSE;
- doPrint = FALSE;
-
- if (argc < 3)
- {
- fprintf(stderr, "Too few arguments for ar\n");
-
- return 1;
- }
-
- /*
- * 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 1;
-
- default:
- fprintf(stderr, "Unknown ar flag: %c\n", *options);
-
- return 1;
- }
- }
-
- if (doExtract + doTable + doPrint != 1)
- {
- fprintf(stderr,
- "Exactly one of 'x', 'p' or 't' must be specified\n");
-
- return 1;
- }
-
- /*
- * Open the archive file.
- */
- initArchive(&arch);
-
- if (!openArchive(archiveName, &arch))
- return 1;
-
- /*
- * Read the first special member of the archive.
- */
- if (!readSpecialMember(&arch))
- return 1;
-
- /*
- * 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)
- {
- r = 1;
- break;
- }
- }
- else
- {
- fprintf(stderr, "Oops -- I don't know what to do\n");
- r = 1;
- break;
- }
- }
-
- closeArchive(&arch);
-
- return r;
-}
-
-
-/*
- * 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.
- */
- checkStatus("fchmod", fchmod(fd, arch->mode));
- checkStatus("fchown", 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)
-{
- 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/src/sash/cmd_chattr.c b/src/sash/cmd_chattr.c
deleted file mode 100644
index df50c22..0000000
--- a/src/sash/cmd_chattr.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (c) 2014 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.
- */
-
-#if HAVE_LINUX_ATTR
-
-#include <sys/ioctl.h>
-#include <sys/types.h>
-
-/*
- * These were used for old linux versions.
- * #include <linux/fs.h>
- * #include <linux/ext2_fs.h>
- */
-
-#include <ext2fs/ext2_fs.h>
-
-
-#include "sash.h"
-
-
-/*
- * The chattr command.
- * This can turn on or off the immutable and append-only ext2 flags.
- */
-int
-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;
- int r;
-
- r = 0;
- 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 1;
- }
- }
- }
-
- /*
- * Make sure that the attributes are reasonable.
- */
- if ((onFlags == 0) && (offFlags == 0))
- {
- fprintf(stderr, "No attributes specified\n");
-
- return 1;
- }
-
- if ((onFlags & offFlags) != 0)
- {
- fprintf(stderr, "Inconsistent attributes specified\n");
-
- return 1;
- }
-
- /*
- * Make sure there are some files to affect.
- */
- if (argc <= 0)
- {
- fprintf(stderr, "No files specified for setting attributes\n");
-
- return 1;
- }
-
- /*
- * 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);
- r = 1;
- continue;
- }
-
- /*
- * Read the current ext2 flag bits.
- */
- if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldFlags) < 0)
- {
- perror(fileName);
- r = 1;
- (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);
- r = 1;
- (void) close(fd);
-
- continue;
- }
-
- /*
- * Close the file.
- */
- (void) close(fd);
- }
-
- return r;
-}
-
-
-/*
- * The lsattr command.
- * This lists the immutable and append-only ext2 flags.
- */
-int
-do_lsattr(int argc, const char ** argv)
-{
- const char * fileName;
- int r;
- int fd;
- int status;
- int flags;
- char string[4];
-
- r = 0;
- 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);
- r = 1;
- 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);
- r = 1;
-
- 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);
- }
-
- return r;
-}
-
-#endif
-
-
-/* END CODE */
diff --git a/src/sash/cmd_dd.c b/src/sash/cmd_dd.c
deleted file mode 100644
index 80338b6..0000000
--- a/src/sash/cmd_dd.c
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "dd" built-in command.
- */
-
-#include "sash.h"
-
-
-#define PAR_NONE 0
-#define PAR_IF 1
-#define PAR_OF 2
-#define PAR_BS 3
-#define PAR_COUNT 4
-#define PAR_SEEK 5
-#define PAR_SKIP 6
-
-
-typedef struct
-{
- const char * name;
- int value;
-} PARAM;
-
-
-static const PARAM params[] =
-{
- {"if", PAR_IF},
- {"of", PAR_OF},
- {"bs", PAR_BS},
- {"count", PAR_COUNT},
- {"seek", PAR_SEEK},
- {"skip", PAR_SKIP},
- {NULL, PAR_NONE}
-};
-
-
-static long getNum(const char * cp);
-
-
-int
-do_dd(int argc, const char ** argv)
-{
- const char * str;
- const PARAM * par;
- const char * inFile;
- const char * outFile;
- char * cp;
- int inFd;
- int outFd;
- int inCc;
- int outCc;
- int blockSize;
- long count;
- long seekVal;
- long skipVal;
- long inFull;
- long inPartial;
- long outFull;
- long outPartial;
- char * buf;
- char localBuf[BUF_SIZE];
- int r;
-
- inFile = NULL;
- outFile = NULL;
- seekVal = 0;
- skipVal = 0;
- blockSize = 512;
- count = -1;
- r = 0;
-
- while (--argc > 0)
- {
- str = *++argv;
- cp = strchr(str, '=');
-
- if (cp == NULL)
- {
- fprintf(stderr, "Bad dd argument\n");
-
- return 1;
- }
-
- *cp++ = '\0';
-
- for (par = params; par->name; par++)
- {
- if (strcmp(str, par->name) == 0)
- break;
- }
-
- switch (par->value)
- {
- case PAR_IF:
- if (inFile)
- {
- fprintf(stderr, "Multiple input files illegal\n");
-
- return 1;
- }
-
- inFile = cp;
- break;
-
- case PAR_OF:
- if (outFile)
- {
- fprintf(stderr, "Multiple output files illegal\n");
-
- return 1;
- }
-
- outFile = cp;
- break;
-
- case PAR_BS:
- blockSize = getNum(cp);
-
- if (blockSize <= 0)
- {
- fprintf(stderr, "Bad block size value\n");
-
- return 1;
- }
-
- break;
-
- case PAR_COUNT:
- count = getNum(cp);
-
- if (count < 0)
- {
- fprintf(stderr, "Bad count value\n");
-
- return 1;
- }
-
- break;
-
- case PAR_SEEK:
- seekVal = getNum(cp);
-
- if (seekVal < 0)
- {
- fprintf(stderr, "Bad seek value\n");
-
- return 1;
- }
-
- break;
-
- case PAR_SKIP:
- skipVal = getNum(cp);
-
- if (skipVal < 0)
- {
- fprintf(stderr, "Bad skip value\n");
-
- return 1;
- }
-
- break;
-
- default:
- fprintf(stderr, "Unknown dd parameter\n");
-
- return 1;
- }
- }
-
- if (inFile == NULL)
- {
- fprintf(stderr, "No input file specified\n");
-
- return 1;
- }
-
- if (outFile == NULL)
- {
- fprintf(stderr, "No output file specified\n");
-
- return 1;
- }
-
- buf = localBuf;
-
- if (blockSize > sizeof(localBuf))
- {
- buf = malloc(blockSize);
-
- if (buf == NULL)
- {
- fprintf(stderr, "Cannot allocate buffer\n");
-
- return 1;
- }
- }
-
- inFull = 0;
- inPartial = 0;
- outFull = 0;
- outPartial = 0;
-
- inFd = open(inFile, 0);
-
- if (inFd < 0)
- {
- perror(inFile);
-
- if (buf != localBuf)
- free(buf);
-
- return 1;
- }
-
- outFd = creat(outFile, 0666);
-
- if (outFd < 0)
- {
- perror(outFile);
- close(inFd);
-
- if (buf != localBuf)
- free(buf);
-
- return 1;
- }
-
- if (skipVal)
- {
- if (lseek(inFd, skipVal * blockSize, 0) < 0)
- {
- while (skipVal-- > 0)
- {
- inCc = read(inFd, buf, blockSize);
-
- if (inCc < 0)
- {
- perror(inFile);
- r = 1;
- goto cleanup;
- }
-
- if (inCc == 0)
- {
- fprintf(stderr, "End of file while skipping\n");
- r = 1;
- goto cleanup;
- }
- }
- }
- }
-
- if (seekVal)
- {
- if (lseek(outFd, seekVal * blockSize, 0) < 0)
- {
- perror(outFile);
- r = 1;
- goto cleanup;
- }
- }
-
- inCc = 0;
-
- while (((count < 0) || (inFull + inPartial < count)) &&
- (inCc = read(inFd, buf, blockSize)) > 0)
- {
- if (inCc < blockSize)
- inPartial++;
- else
- inFull++;
- cp = buf;
-
- if (intFlag)
- {
- fprintf(stderr, "Interrupted\n");
- r = 1;
- goto cleanup;
- }
-
- while (inCc > 0)
- {
- outCc = write(outFd, cp, inCc);
-
- if (outCc < 0)
- {
- perror(outFile);
- r = 1;
- goto cleanup;
- }
-
- if (outCc < blockSize)
- outPartial++;
- else
- outFull++;
- cp += outCc;
- inCc -= outCc;
- }
- }
-
- if (inCc < 0)
- perror(inFile);
-
-cleanup:
- close(inFd);
-
- if (close(outFd) < 0)
- {
- perror(outFile);
- r = 1;
- }
-
- if (buf != localBuf)
- free(buf);
-
- printf("%ld+%ld records in\n", inFull, inPartial);
-
- printf("%ld+%ld records out\n", outFull, outPartial);
-
- return r;
-}
-
-
-/*
- * Read a number with a possible multiplier.
- * Returns -1 if the number format is illegal.
- */
-static long
-getNum(const char * cp)
-{
- long value;
-
- if (!isDecimal(*cp))
- return -1;
-
- value = 0;
-
- while (isDecimal(*cp))
- value = value * 10 + *cp++ - '0';
-
- switch (*cp++)
- {
- case 'k':
- value *= 1024;
- break;
-
- case 'b':
- value *= 512;
- break;
-
- case 'w':
- value *= 2;
- break;
-
- case '\0':
- return value;
-
- default:
- return -1;
- }
-
- if (*cp)
- return -1;
-
- return value;
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_ed.c b/src/sash/cmd_ed.c
deleted file mode 100644
index e935c0d..0000000
--- a/src/sash/cmd_ed.c
+++ /dev/null
@@ -1,1440 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "ed" built-in command (much simplified)
- */
-
-#include "sash.h"
-
-#define USERSIZE 1024 /* max line length typed in by user */
-#define INITBUF_SIZE 1024 /* initial buffer size */
-
-
-typedef int NUM;
-typedef int LEN;
-
-typedef struct LINE LINE;
-
-struct LINE
-{
- LINE * next;
- LINE * prev;
- LEN len;
- char data[1];
-};
-
-
-static LINE lines;
-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(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);
-
-
-int
-do_ed(int argc, const char ** argv)
-{
- if (!initEdit())
- return 1;
-
- if (argc > 1)
- {
- fileName = strdup(argv[1]);
-
- if (fileName == NULL)
- {
- fprintf(stderr, "No memory\n");
- termEdit();
-
- return 1;
- }
-
- if (!readLines(fileName, 1))
- {
- termEdit();
-
- return 1;
- }
-
- if (lastNum)
- setCurNum(1);
-
- dirty = FALSE;
- }
-
- doCommands();
-
- termEdit();
- return 0;
-}
-
-
-/*
- * Read commands until we are told to stop.
- */
-static void
-doCommands(void)
-{
- const char * cp;
- char * endbuf;
- char * newname;
- int len;
- NUM num1;
- NUM num2;
- BOOL have1;
- BOOL have2;
- char buf[USERSIZE];
-
- while (TRUE)
- {
- intFlag = FALSE;
- printf(": ");
- fflush(stdout);
-
- if (fgets(buf, sizeof(buf), stdin) == NULL)
- return;
-
- len = strlen(buf);
-
- if (len == 0)
- return;
-
- endbuf = &buf[len - 1];
-
- if (*endbuf != '\n')
- {
- fprintf(stderr, "Command line too long\n");
-
- do
- {
- len = fgetc(stdin);
- }
- while ((len != EOF) && (len != '\n'));
-
- continue;
- }
-
- while ((endbuf > buf) && isBlank(endbuf[-1]))
- endbuf--;
-
- *endbuf = '\0';
-
- cp = buf;
-
- while (isBlank(*cp))
- cp++;
-
- have1 = FALSE;
- have2 = FALSE;
-
- if ((curNum == 0) && (lastNum > 0))
- {
- curNum = 1;
- curLine = lines.next;
- }
-
- if (!getNum(&cp, &have1, &num1))
- continue;
-
- while (isBlank(*cp))
- cp++;
-
- if (*cp == ',')
- {
- cp++;
-
- if (!getNum(&cp, &have2, &num2))
- continue;
-
- if (!have1)
- num1 = 1;
-
- if (!have2)
- num2 = lastNum;
-
- have1 = TRUE;
- have2 = TRUE;
- }
-
- if (!have1)
- num1 = curNum;
-
- if (!have2)
- num2 = num1;
-
- switch (*cp++)
- {
- case 'a':
- addLines(num1 + 1);
- break;
-
- case 'c':
- deleteLines(num1, num2);
- addLines(num1);
- break;
-
- case 'd':
- deleteLines(num1, num2);
- break;
-
- case 'f':
- if (*cp && !isBlank(*cp))
- {
- fprintf(stderr, "Bad file command\n");
- break;
- }
-
- while (isBlank(*cp))
- cp++;
-
- if (*cp == '\0')
- {
- if (fileName)
- printf("\"%s\"\n", fileName);
- else
- printf("No file name\n");
-
- break;
- }
-
- newname = strdup(cp);
-
- if (newname == NULL)
- {
- fprintf(stderr, "No memory for file name\n");
- break;
- }
-
- if (fileName)
- free(fileName);
-
- fileName = newname;
- break;
-
- case 'i':
- addLines(num1);
- break;
-
- case 'k':
- while (isBlank(*cp))
- cp++;
-
- if ((*cp < 'a') || (*cp > 'a') || cp[1])
- {
- fprintf(stderr, "Bad mark name\n");
- break;
- }
-
- marks[*cp - 'a'] = num2;
- break;
-
- case 'l':
- printLines(num1, num2, TRUE);
- break;
-
- case 'p':
- printLines(num1, num2, FALSE);
- break;
-
- case 'q':
- while (isBlank(*cp))
- cp++;
-
- if (have1 || *cp)
- {
- fprintf(stderr, "Bad quit command\n");
- break;
- }
-
- if (!dirty)
- return;
-
- printf("Really quit? ");
- fflush(stdout);
-
- buf[0] = '\0';
-
- if (fgets(buf, sizeof(buf), stdin) == NULL)
- return;
-
- cp = buf;
-
- while (isBlank(*cp))
- cp++;
-
- if ((*cp == 'y') || (*cp == 'Y'))
- return;
-
- break;
-
- case 'r':
- if (*cp && !isBlank(*cp))
- {
- fprintf(stderr, "Bad read command\n");
- break;
- }
-
- while (isBlank(*cp))
- cp++;
-
- if (*cp == '\0')
- {
- fprintf(stderr, "No file name\n");
- break;
- }
-
- if (!have1)
- num1 = lastNum;
-
- if (readLines(cp, num1 + 1))
- break;
-
- if (fileName == NULL)
- fileName = strdup(cp);
-
- break;
-
- case 's':
- subCommand(cp, num1, num2);
- break;
-
- case 'w':
- if (*cp && !isBlank(*cp))
- {
- fprintf(stderr, "Bad write command\n");
- break;
- }
-
- while (isBlank(*cp))
- cp++;
-
- if (!have1) {
- num1 = 1;
- num2 = lastNum;
- }
-
- if (*cp == '\0')
- cp = fileName;
-
- if (cp == NULL)
- {
- fprintf(stderr, "No file name specified\n");
- break;
- }
-
- writeLines(cp, num1, num2);
- break;
-
- case 'z':
- switch (*cp)
- {
- case '-':
- printLines(curNum-21, curNum, FALSE);
- break;
- case '.':
- printLines(curNum-11, curNum+10, FALSE);
- break;
- default:
- printLines(curNum, curNum+21, FALSE);
- break;
- }
- break;
-
- case '.':
- if (have1)
- {
- fprintf(stderr, "No arguments allowed\n");
- break;
- }
-
- printLines(curNum, curNum, FALSE);
- break;
-
- case '-':
- if (setCurNum(curNum - 1))
- printLines(curNum, curNum, FALSE);
-
- break;
-
- case '=':
- printf("%d\n", num1);
- break;
-
- case '\0':
- if (have1)
- {
- printLines(num2, num2, FALSE);
- break;
- }
-
- if (setCurNum(curNum + 1))
- printLines(curNum, curNum, FALSE);
-
- break;
-
- default:
- fprintf(stderr, "Unimplemented command\n");
- break;
- }
- }
-}
-
-
-/*
- * Do the substitute command.
- * The current line is set to the last substitution done.
- */
-static void
-subCommand(const char * cmd, NUM num1, NUM num2)
-{
- int delim;
- char * cp;
- char * oldStr;
- char * newStr;
- LEN oldLen;
- LEN newLen;
- LEN deltaLen;
- LEN offset;
- LINE * lp;
- LINE * nlp;
- BOOL globalFlag;
- BOOL printFlag;
- BOOL didSub;
- BOOL needPrint;
- char buf[USERSIZE];
-
- if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
- {
- fprintf(stderr, "Bad line range for substitute\n");
-
- return;
- }
-
- globalFlag = FALSE;
- printFlag = FALSE;
- didSub = FALSE;
- needPrint = FALSE;
-
- /*
- * Copy the command so we can modify it.
- */
- strcpy(buf, cmd);
- cp = buf;
-
- if (isBlank(*cp) || (*cp == '\0'))
- {
- fprintf(stderr, "Bad delimiter for substitute\n");
-
- return;
- }
-
- delim = *cp++;
- oldStr = cp;
-
- cp = strchr(cp, delim);
-
- if (cp == NULL)
- {
- fprintf(stderr, "Missing 2nd delimiter for substitute\n");
-
- return;
- }
-
- *cp++ = '\0';
-
- newStr = cp;
- cp = strchr(cp, delim);
-
- if (cp)
- *cp++ = '\0';
- else
- cp = "";
-
- while (*cp) switch (*cp++)
- {
- case 'g':
- globalFlag = TRUE;
- break;
-
- case 'p':
- printFlag = TRUE;
- break;
-
- default:
- fprintf(stderr, "Unknown option for substitute\n");
-
- return;
- }
-
- if (*oldStr == '\0')
- {
- if (searchString[0] == '\0')
- {
- fprintf(stderr, "No previous search string\n");
-
- return;
- }
-
- oldStr = searchString;
- }
-
- if (oldStr != searchString)
- strcpy(searchString, oldStr);
-
- lp = findLine(num1);
-
- if (lp == NULL)
- return;
-
- oldLen = strlen(oldStr);
- newLen = strlen(newStr);
- deltaLen = newLen - oldLen;
- offset = 0;
- nlp = NULL;
-
- while (num1 <= num2)
- {
- offset = findString(lp, oldStr, oldLen, offset);
-
- if (offset < 0)
- {
- if (needPrint)
- {
- printLines(num1, num1, FALSE);
- needPrint = FALSE;
- }
-
- offset = 0;
- lp = lp->next;
- num1++;
-
- continue;
- }
-
- 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)
- {
- memcpy(&lp->data[offset + newLen],
- &lp->data[offset + oldLen],
- lp->len - offset - oldLen);
-
- lp->len += deltaLen;
- }
-
- offset += newLen;
-
- if (globalFlag)
- continue;
-
- if (needPrint)
- {
- printLines(num1, num1, FALSE);
- needPrint = FALSE;
- }
-
- lp = lp->next;
- num1++;
-
- continue;
- }
-
- /*
- * The new string is larger, so allocate a new line
- * structure and use that. Link it in in place of
- * the old line structure.
- */
- nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
-
- if (nlp == NULL)
- {
- fprintf(stderr, "Cannot get memory for line\n");
-
- return;
- }
-
- nlp->len = lp->len + deltaLen;
-
- memcpy(nlp->data, lp->data, offset);
-
- memcpy(&nlp->data[offset], newStr, newLen);
-
- 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;
-
- free(lp);
- lp = nlp;
-
- offset += newLen;
-
- if (globalFlag)
- continue;
-
- if (needPrint)
- {
- printLines(num1, num1, FALSE);
- needPrint = FALSE;
- }
-
- lp = lp->next;
- num1++;
- }
-
- if (!didSub)
- fprintf(stderr, "No substitutions found for \"%s\"\n", oldStr);
-}
-
-
-/*
- * Search a line for the specified string starting at the specified
- * offset in the line. Returns the offset of the found string, or -1.
- */
-static LEN
-findString( const LINE * lp, const char * str, LEN len, LEN offset)
-{
- LEN left;
- const char * cp;
- const char * ncp;
-
- cp = &lp->data[offset];
- left = lp->len - offset;
-
- while (left >= len)
- {
- ncp = memchr(cp, *str, left);
-
- if (ncp == NULL)
- return -1;
-
- left -= (ncp - cp);
-
- if (left < len)
- return -1;
-
- cp = ncp;
-
- if (memcmp(cp, str, len) == 0)
- return (cp - lp->data);
-
- cp++;
- left--;
- }
-
- return -1;
-}
-
-
-/*
- * Add lines which are typed in by the user.
- * The lines are inserted just before the specified line number.
- * The lines are terminated by a line containing a single dot (ugly!),
- * or by an end of file.
- */
-static void
-addLines(NUM num)
-{
- int len;
- char buf[USERSIZE + 1];
-
- while (fgets(buf, sizeof(buf), stdin))
- {
- if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
- return;
-
- len = strlen(buf);
-
- if (len == 0)
- return;
-
- if (buf[len - 1] != '\n')
- {
- fprintf(stderr, "Line too long\n");
-
- do
- {
- len = fgetc(stdin);
- }
- while ((len != EOF) && (len != '\n'));
-
- return;
- }
-
- if (!insertLine(num++, buf, len))
- return;
- }
-}
-
-
-/*
- * Parse a line number argument if it is present. This is a sum
- * or difference of numbers, '.', '$', 'x, or a search string.
- * Returns TRUE if successful (whether or not there was a number).
- * Returns FALSE if there was a parsing error, with a message output.
- * Whether there was a number is returned indirectly, as is the number.
- * The character pointer which stopped the scan is also returned.
- */
-static BOOL
-getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
-{
- const char * cp;
- char * endStr;
- char str[USERSIZE];
- BOOL haveNum;
- NUM value;
- NUM num;
- NUM sign;
-
- cp = *retcp;
- haveNum = FALSE;
- value = 0;
- sign = 1;
-
- while (TRUE)
- {
- while (isBlank(*cp))
- cp++;
-
- switch (*cp)
- {
- case '.':
- haveNum = TRUE;
- num = curNum;
- cp++;
- break;
-
- case '$':
- haveNum = TRUE;
- num = lastNum;
- cp++;
- break;
-
- case '\'':
- cp++;
-
- if ((*cp < 'a') || (*cp > 'z'))
- {
- fprintf(stderr, "Bad mark name\n");
-
- return FALSE;
- }
-
- haveNum = TRUE;
- num = marks[*cp++ - 'a'];
- break;
-
- case '/':
- strcpy(str, ++cp);
- endStr = strchr(str, '/');
-
- if (endStr)
- {
- *endStr++ = '\0';
- cp += (endStr - str);
- }
- else
- cp = "";
-
- num = searchLines(str, curNum, lastNum);
-
- if (num == 0)
- return FALSE;
-
- haveNum = TRUE;
- break;
-
- default:
- if (!isDecimal(*cp))
- {
- *retcp = cp;
- *retHaveNum = haveNum;
- *retNum = value;
-
- return TRUE;
- }
-
- num = 0;
-
- while (isDecimal(*cp))
- num = num * 10 + *cp++ - '0';
-
- haveNum = TRUE;
- break;
- }
-
- value += num * sign;
-
- while (isBlank(*cp))
- cp++;
-
- switch (*cp)
- {
- case '-':
- sign = -1;
- cp++;
- break;
-
- case '+':
- sign = 1;
- cp++;
- break;
-
- default:
- *retcp = cp;
- *retHaveNum = haveNum;
- *retNum = value;
-
- return TRUE;
- }
- }
-}
-
-
-/*
- * Initialize everything for editing.
- */
-static BOOL
-initEdit(void)
-{
- int i;
-
- bufSize = INITBUF_SIZE;
- bufBase = malloc(bufSize);
-
- if (bufBase == NULL)
- {
- fprintf(stderr, "No memory for buffer\n");
-
- return FALSE;
- }
-
- bufPtr = bufBase;
- bufUsed = 0;
-
- lines.next = &lines;
- lines.prev = &lines;
-
- curLine = NULL;
- curNum = 0;
- lastNum = 0;
- dirty = FALSE;
- fileName = NULL;
- searchString[0] = '\0';
-
- for (i = 0; i < 26; i++)
- marks[i] = 0;
-
- return TRUE;
-}
-
-
-/*
- * Finish editing.
- */
-static void
-termEdit(void)
-{
- if (bufBase)
- free(bufBase);
-
- bufBase = NULL;
- bufPtr = NULL;
- bufSize = 0;
- bufUsed = 0;
-
- if (fileName)
- free(fileName);
-
- fileName = NULL;
-
- searchString[0] = '\0';
-
- if (lastNum)
- deleteLines(1, lastNum);
-
- lastNum = 0;
- curNum = 0;
- curLine = NULL;
-}
-
-
-/*
- * Read lines from a file at the specified line number.
- * Returns TRUE if the file was successfully read.
- */
-static BOOL
-readLines(const char * file, NUM num)
-{
- int fd;
- int cc;
- LEN len;
- LEN lineCount;
- LEN charCount;
- char * cp;
-
- if ((num < 1) || (num > lastNum + 1))
- {
- fprintf(stderr, "Bad line for read\n");
-
- return FALSE;
- }
-
- fd = open(file, 0);
-
- if (fd < 0)
- {
- perror(file);
-
- return FALSE;
- }
-
- bufPtr = bufBase;
- bufUsed = 0;
- lineCount = 0;
- charCount = 0;
- cc = 0;
-
- printf("\"%s\", ", file);
- fflush(stdout);
-
- do
- {
- if (intFlag)
- {
- printf("INTERRUPTED, ");
- bufUsed = 0;
- break;
- }
-
- cp = memchr(bufPtr, '\n', bufUsed);
-
- if (cp)
- {
- len = (cp - bufPtr) + 1;
-
- if (!insertLine(num, bufPtr, len))
- {
- close(fd);
-
- return FALSE;
- }
-
- bufPtr += len;
- bufUsed -= len;
- charCount += len;
- lineCount++;
- num++;
-
- continue;
- }
-
- if (bufPtr != bufBase)
- {
- memcpy(bufBase, bufPtr, bufUsed);
- bufPtr = bufBase + bufUsed;
- }
-
- if (bufUsed >= bufSize)
- {
- len = (bufSize * 3) / 2;
- cp = realloc(bufBase, len);
-
- if (cp == NULL)
- {
- fprintf(stderr, "No memory for buffer\n");
- close(fd);
-
- return FALSE;
- }
-
- bufBase = cp;
- bufPtr = bufBase + bufUsed;
- bufSize = len;
- }
-
- cc = read(fd, bufPtr, bufSize - bufUsed);
- bufUsed += cc;
- bufPtr = bufBase;
-
- }
- while (cc > 0);
-
- if (cc < 0)
- {
- perror(file);
- close(fd);
-
- return FALSE;
- }
-
- if (bufUsed)
- {
- if (!insertLine(num, bufPtr, bufUsed))
- {
- close(fd);
-
- return -1;
- }
-
- lineCount++;
- charCount += bufUsed;
- }
-
- close(fd);
-
- printf("%d lines%s, %d chars\n", lineCount,
- (bufUsed ? " (incomplete)" : ""), charCount);
-
- return TRUE;
-}
-
-
-/*
- * Write the specified lines out to the specified file.
- * Returns TRUE if successful, or FALSE on an error with a message output.
- */
-static BOOL
-writeLines(const char * file, NUM num1, NUM num2)
-{
- int fd;
- LINE * lp;
- LEN lineCount;
- LEN charCount;
-
- if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
- {
- fprintf(stderr, "Bad line range for write\n");
-
- return FALSE;
- }
-
- lineCount = 0;
- charCount = 0;
-
- fd = creat(file, 0666);
-
- if (fd < 0) {
- perror(file);
-
- return FALSE;
- }
-
- printf("\"%s\", ", file);
- fflush(stdout);
-
- lp = findLine(num1);
-
- if (lp == NULL)
- {
- close(fd);
-
- return FALSE;
- }
-
- while (num1++ <= num2)
- {
- if (write(fd, lp->data, lp->len) != lp->len)
- {
- perror(file);
- close(fd);
-
- return FALSE;
- }
-
- charCount += lp->len;
- lineCount++;
- lp = lp->next;
- }
-
- if (close(fd) < 0)
- {
- perror(file);
-
- return FALSE;
- }
-
- printf("%d lines, %d chars\n", lineCount, charCount);
-
- return TRUE;
-}
-
-
-/*
- * 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
- * show magic characters.
- */
-static BOOL
-printLines(NUM num1, NUM num2, BOOL expandFlag)
-{
- const LINE * lp;
- const char * cp;
- int ch;
- LEN count;
-
- if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
- {
- fprintf(stderr, "Bad line range for print\n");
-
- return FALSE;
- }
-
- lp = findLine(num1);
-
- if (lp == NULL)
- return FALSE;
-
- while (!intFlag && (num1 <= num2))
- {
- if (!expandFlag)
- {
- tryWrite(STDOUT, lp->data, lp->len);
- setCurNum(num1++);
- lp = lp->next;
-
- continue;
- }
-
- /*
- * Show control characters and characters with the
- * high bit set specially.
- */
- cp = lp->data;
- count = lp->len;
-
- if ((count > 0) && (cp[count - 1] == '\n'))
- count--;
-
- while (count-- > 0)
- {
- ch = *cp++ & 0xff;
-
- if (ch & 0x80)
- {
- fputs("M-", stdout);
- ch &= 0x7f;
- }
-
- if (ch < ' ')
- {
- fputc('^', stdout);
- ch += '@';
- }
-
- if (ch == 0x7f)
- {
- fputc('^', stdout);
- ch = '?';
- }
-
- fputc(ch, stdout);
- }
-
- fputs("$\n", stdout);
-
- setCurNum(num1++);
- lp = lp->next;
- }
-
- return TRUE;
-}
-
-
-/*
- * Insert a new line with the specified text.
- * The line is inserted so as to become the specified line,
- * thus pushing any existing and further lines down one.
- * The inserted line is also set to become the current line.
- * Returns TRUE if successful.
- */
-static BOOL
-insertLine(NUM num, const char * data, LEN len)
-{
- LINE * newLp;
- LINE * lp;
-
- if ((num < 1) || (num > lastNum + 1))
- {
- fprintf(stderr, "Inserting at bad line number\n");
-
- return FALSE;
- }
-
- newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
-
- if (newLp == NULL)
- {
- fprintf(stderr, "Failed to allocate memory for line\n");
-
- return FALSE;
- }
-
- memcpy(newLp->data, data, len);
- newLp->len = len;
-
- if (num > lastNum)
- lp = &lines;
- else
- {
- lp = findLine(num);
-
- if (lp == NULL)
- {
- free((char *) newLp);
-
- return FALSE;
- }
- }
-
- newLp->next = lp;
- newLp->prev = lp->prev;
- lp->prev->next = newLp;
- lp->prev = newLp;
-
- lastNum++;
- dirty = TRUE;
-
- return setCurNum(num);
-}
-
-
-/*
- * Delete lines from the given range.
- */
-static BOOL
-deleteLines(NUM num1, NUM num2)
-{
- LINE * lp;
- LINE * nlp;
- LINE * plp;
- NUM count;
-
- if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
- {
- fprintf(stderr, "Bad line numbers for delete\n");
-
- return FALSE;
- }
-
- lp = findLine(num1);
-
- if (lp == NULL)
- return FALSE;
-
- if ((curNum >= num1) && (curNum <= num2))
- {
- if (num2 < lastNum)
- setCurNum(num2 + 1);
- else if (num1 > 1)
- setCurNum(num1 - 1);
- else
- curNum = 0;
- }
-
- count = num2 - num1 + 1;
-
- if (curNum > num2)
- curNum -= count;
-
- lastNum -= count;
-
- while (count-- > 0)
- {
- nlp = lp->next;
- plp = lp->prev;
- plp->next = nlp;
- nlp->prev = plp;
- lp->next = NULL;
- lp->prev = NULL;
- lp->len = 0;
- free(lp);
- lp = nlp;
- }
-
- dirty = TRUE;
-
- return TRUE;
-}
-
-
-/*
- * Search for a line which contains the specified string.
- * If the string is NULL, then the previously searched for string
- * is used. The currently searched for string is saved for future use.
- * Returns the line number which matches, or 0 if there was no match
- * with an error printed.
- */
-static NUM
-searchLines(const char * str, NUM num1, NUM num2)
-{
- const LINE * lp;
- int len;
-
- if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
- {
- fprintf(stderr, "Bad line numbers for search\n");
-
- return 0;
- }
-
- if (*str == '\0')
- {
- if (searchString[0] == '\0')
- {
- fprintf(stderr, "No previous search string\n");
-
- return 0;
- }
-
- str = searchString;
- }
-
- if (str != searchString)
- strcpy(searchString, str);
-
- len = strlen(str);
-
- lp = findLine(num1);
-
- if (lp == NULL)
- return 0;
-
- while (num1 <= num2)
- {
- if (findString(lp, str, len, 0) >= 0)
- return num1;
-
- num1++;
- lp = lp->next;
- }
-
- fprintf(stderr, "Cannot find string \"%s\"\n", str);
-
- return 0;
-}
-
-
-/*
- * Return a pointer to the specified line number.
- */
-static LINE *
-findLine(NUM num)
-{
- LINE * lp;
- NUM lnum;
-
- 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 (num == curNum)
- return curLine;
-
- lp = curLine;
- lnum = curNum;
-
- if (num < (curNum / 2))
- {
- lp = lines.next;
- lnum = 1;
- }
- else if (num > ((curNum + lastNum) / 2))
- {
- lp = lines.prev;
- lnum = lastNum;
- }
-
- while (lnum < num)
- {
- lp = lp->next;
- lnum++;
- }
-
- while (lnum > num)
- {
- lp = lp->prev;
- lnum--;
- }
-
- return lp;
-}
-
-
-/*
- * Set the current line number.
- * Returns TRUE if successful.
- */
-static BOOL
-setCurNum(NUM num)
-{
- LINE * lp;
-
- lp = findLine(num);
-
- if (lp == NULL)
- return FALSE;
-
- curNum = num;
- curLine = lp;
-
- return TRUE;
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_file.c b/src/sash/cmd_file.c
deleted file mode 100644
index caf2a30..0000000
--- a/src/sash/cmd_file.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 2014 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);
-
-
-int
-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);
- }
-
- return 0;
-}
-
-
-/*
- * 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/src/sash/cmd_find.c b/src/sash/cmd_find.c
deleted file mode 100644
index fde9006..0000000
--- a/src/sash/cmd_find.c
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright (c) 2014 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.
- */
-int
-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 1;
- }
-
- 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 1;
- }
-
- argc--;
- fileType = *argv++;
- }
- else if (strcmp(cp, "-name") == 0)
- {
- if ((argc <= 0) || (**argv == '-'))
- {
- fprintf(stderr, "Missing file name\n");
-
- return 1;
- }
-
- argc--;
- filePattern = *argv++;
- }
- else if (strcmp(cp, "-size") == 0)
- {
- if ((argc <= 0) || (**argv == '-'))
- {
- fprintf(stderr, "Missing file size\n");
-
- return 1;
- }
-
- 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 1;
- }
- }
- else
- {
- if (*cp != '-')
- fprintf(stderr, "Missing dash in option\n");
- else
- fprintf(stderr, "Unknown option\n");
-
- return 1;
- }
- }
-
- /*
- * 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 1;
- }
-
- if (!S_ISDIR(statBuf.st_mode))
- {
- fprintf(stderr, "Path \"%s\" is not a directory\n", path);
-
- return 1;
- }
-
- /*
- * 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);
-
- return 0;
-}
-
-
-/*
- * 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 */
diff --git a/src/sash/cmd_grep.c b/src/sash/cmd_grep.c
deleted file mode 100644
index b9e0821..0000000
--- a/src/sash/cmd_grep.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "grep" built-in command.
- */
-
-#include <ctype.h>
-
-#include "sash.h"
-
-
-static BOOL search
- (const char * string, const char * word, BOOL ignoreCase);
-
-
-int
-do_grep(int argc, const char ** argv)
-{
- FILE * fp;
- const char * word;
- const char * name;
- const char * cp;
- BOOL tellName;
- BOOL ignoreCase;
- BOOL tellLine;
- long line;
- char buf[BUF_SIZE];
- int r;
-
- r = 1;
- ignoreCase = FALSE;
- tellLine = FALSE;
-
- argc--;
- argv++;
-
- if (**argv == '-')
- {
- argc--;
- cp = *argv++;
-
- while (*++cp) switch (*cp)
- {
- case 'i':
- ignoreCase = TRUE;
- break;
-
- case 'n':
- tellLine = TRUE;
- break;
-
- default:
- fprintf(stderr, "Unknown option\n");
-
- return 1;
- }
- }
-
- word = *argv++;
- argc--;
-
- tellName = (argc > 1);
-
- while (argc-- > 0)
- {
- name = *argv++;
-
- fp = fopen(name, "r");
-
- if (fp == NULL)
- {
- perror(name);
- r = 1;
-
- continue;
- }
-
- line = 0;
-
- while (fgets(buf, sizeof(buf), fp))
- {
- if (intFlag)
- {
- fclose(fp);
-
- return 1;
- }
-
- line++;
-
- cp = &buf[strlen(buf) - 1];
-
- if (*cp != '\n')
- fprintf(stderr, "%s: Line too long\n", name);
-
- if (search(buf, word, ignoreCase))
- {
- r = 0;
- if (tellName)
- printf("%s: ", name);
-
- if (tellLine)
- printf("%ld: ", line);
-
- fputs(buf, stdout);
- }
- }
-
- if (ferror(fp))
- perror(name);
-
- fclose(fp);
- }
-
- return r;
-}
-
-
-/*
- * See if the specified word is found in the specified string.
- */
-static BOOL
-search(const char * string, const char * word, BOOL ignoreCase)
-{
- const char * cp1;
- const char * cp2;
- int len;
- int lowFirst;
- int ch1;
- int ch2;
-
- len = strlen(word);
-
- if (!ignoreCase)
- {
- while (TRUE)
- {
- string = strchr(string, word[0]);
-
- if (string == NULL)
- return FALSE;
-
- if (memcmp(string, word, len) == 0)
- return TRUE;
-
- string++;
- }
- }
-
- /*
- * Here if we need to check case independence.
- * Do the search by lower casing both strings.
- */
- lowFirst = *word;
-
- if (isupper(lowFirst))
- lowFirst = tolower(lowFirst);
-
- while (TRUE)
- {
- while (*string && (*string != lowFirst) &&
- (!isupper(*string) || (tolower(*string) != lowFirst)))
- {
- string++;
- }
-
- if (*string == '\0')
- return FALSE;
-
- cp1 = string;
- cp2 = word;
-
- do
- {
- if (*cp2 == '\0')
- return TRUE;
-
- ch1 = *cp1++;
-
- if (isupper(ch1))
- ch1 = tolower(ch1);
-
- ch2 = *cp2++;
-
- if (isupper(ch2))
- ch2 = tolower(ch2);
-
- }
- while (ch1 == ch2);
-
- string++;
- }
-}
-
-/* END CODE */
diff --git a/src/sash/cmd_gzip.c b/src/sash/cmd_gzip.c
deleted file mode 100644
index 6bcec11..0000000
--- a/src/sash/cmd_gzip.c
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "gzip" and "gunzip" built-in commands.
- * These commands are optionally built into sash.
- * This uses the zlib library by Jean-loup Gailly to compress and
- * uncompress the files.
- */
-
-#if HAVE_GZIP
-
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <zlib.h>
-
-#include "sash.h"
-
-
-#define GZ_EXT ".gz"
-#define TGZ_EXT ".tgz"
-#define Z_EXT ".Z"
-#define TAR_EXT ".tar"
-#define NO_EXT ""
-
-
-/*
- * Tables of conversions to make to file extensions.
- */
-typedef struct
-{
- const char * input;
- const char * output;
-} CONVERT;
-
-
-static const CONVERT gzipConvertTable[] =
-{
- {TAR_EXT, TGZ_EXT},
- {NO_EXT, GZ_EXT}
-};
-
-
-static const CONVERT gunzipConvertTable[] =
-{
- {TGZ_EXT, TAR_EXT},
- {GZ_EXT, NO_EXT},
- {Z_EXT, NO_EXT},
- {NO_EXT, NO_EXT}
-};
-
-
-/*
- * Local routines to compress and uncompress files.
- */
-static BOOL gzip(const char * inputFile, const char * outputFile);
-static BOOL gunzip(const char * inputFile, const char * outputFile);
-
-static const char * convertName
- (const CONVERT * table, const char * inFile);
-
-
-int
-do_gzip(int argc, const char ** argv)
-{
- const char * outPath;
- const char * inFile;
- const char * outFile;
- int i;
- int r;
-
- r = 0;
- argc--;
- argv++;
-
- /*
- * Look for the -o option if it is present.
- * If present, it must be at the end of the command.
- * Remember the output path and remove it if found.
- */
- outPath = NULL;
-
- if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
- (argv[argc - 1][0] != '-'))
- {
- argc -= 2;
- outPath = argv[argc + 1];
- }
-
- /*
- * Now make sure that there are no more options.
- */
- for (i = 0; i < argc; i++)
- {
- if (argv[i][0] == '-')
- {
- if (strcmp(argv[i], "-o") == 0)
- fprintf(stderr, "Illegal use of -o\n");
- else
- fprintf(stderr, "Illegal option\n");
-
- return 1;
- }
- }
-
- /*
- * If there is no output path specified, then compress each of
- * the input files in place using their full paths. The input
- * file names are then deleted.
- */
- if (outPath == NULL)
- {
- while (!intFlag && (argc-- > 0))
- {
- inFile = *argv++;
-
- outFile = convertName(gzipConvertTable, inFile);
-
- /*
- * Try to compress the file.
- */
- if (!gzip(inFile, outFile))
- {
- r = 1;
-
- continue;
- }
-
- /*
- * This was successful.
- * Try to delete the original file now.
- */
- if (unlink(inFile) < 0)
- {
- fprintf(stderr, "%s: %s\n", inFile,
- "Compressed ok but unlink failed");
-
- r = 1;
- }
- }
-
- return r;
- }
-
- /*
- * There is an output path specified.
- * If it is not a directory, then either compress the single
- * specified input file to the exactly specified output path,
- * or else complain.
- */
- if (!isDirectory(outPath))
- {
- if (argc == 1)
- r = !gzip(*argv, outPath);
- else
- {
- fprintf(stderr, "Exactly one input file is required\n");
- r = 1;
- }
-
- return r;
- }
-
- /*
- * There was an output directory specified.
- * Compress each of the input files into the specified
- * output directory, converting their extensions if possible.
- */
- while (!intFlag && (argc-- > 0))
- {
- inFile = *argv++;
-
- /*
- * Strip the path off of the input file name to make
- * the beginnings of the output file name.
- */
- outFile = strrchr(inFile, '/');
-
- if (outFile)
- outFile++;
- else
- outFile = inFile;
-
- /*
- * Convert the extension of the output file name if possible.
- * If we can't, then that is ok.
- */
- outFile = convertName(gzipConvertTable, outFile);
-
- /*
- * Now build the output path name by prefixing it with
- * the output directory.
- */
- outFile = buildName(outPath, outFile);
-
- /*
- * Compress the input file without deleting the input file.
- */
- if (!gzip(inFile, outFile))
- r = 1;
- }
-
- return r;
-}
-
-
-int
-do_gunzip(int argc, const char ** argv)
-{
- const char * outPath;
- const char * inFile;
- const char * outFile;
- int i;
- int r;
-
- r = 0;
- argc--;
- argv++;
-
- /*
- * Look for the -o option if it is present.
- * If present, it must be at the end of the command.
- * Remember the output path and remove it if found.
- */
- outPath = NULL;
-
- if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
- (argv[argc - 1][0] != '-'))
- {
- argc -= 2;
- outPath = argv[argc + 1];
- }
-
- /*
- * Now make sure that there are no more options.
- */
- for (i = 0; i < argc; i++)
- {
- if (argv[i][0] == '-')
- {
- if (strcmp(argv[i], "-o") == 0)
- fprintf(stderr, "Illegal use of -o\n");
- else
- fprintf(stderr, "Illegal option\n");
-
- return 1;
- }
- }
-
- /*
- * If there is no output path specified, then uncompress each of
- * the input files in place using their full paths. They must
- * have one of the proper compression extensions which is converted.
- * The input file names are then deleted.
- */
- if (outPath == NULL)
- {
- while (!intFlag && (argc-- > 0))
- {
- inFile = *argv++;
-
- outFile = convertName(gunzipConvertTable, inFile);
-
- if (inFile == outFile)
- {
- fprintf(stderr, "%s: %s\n", inFile,
- "missing compression extension");
- r = 1;
-
- continue;
- }
-
- /*
- * Try to uncompress the file.
- */
- if (!gunzip(inFile, outFile))
- {
- r = 1;
- continue;
- }
-
- /*
- * This was successful.
- * Try to delete the original file now.
- */
- if (unlink(inFile) < 0)
- {
- fprintf(stderr, "%s: %s\n", inFile,
- "Uncompressed ok but unlink failed");
- r = 1;
- }
- }
-
- return r;
- }
-
- /*
- * There is an output path specified.
- * 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))
- {
- if (!gunzip(*argv++, outPath))
- r = 1;
- }
-
- return r;
- }
-
- /*
- * 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 (!isDirectory(outPath))
- {
- if (argc == 1)
- return !gunzip(*argv, outPath);
- else
- fprintf(stderr, "Exactly one input file is required\n");
-
- return 1;
- }
-
- /*
- * There was an output directory specified.
- * Uncompress each of the input files into the specified
- * output directory, converting their extensions if possible.
- */
- while (!intFlag && (argc-- > 0))
- {
- inFile = *argv++;
-
- /*
- * Strip the path off of the input file name to make
- * the beginnings of the output file name.
- */
- outFile = strrchr(inFile, '/');
-
- if (outFile)
- outFile++;
- else
- outFile = inFile;
-
- /*
- * Convert the extension of the output file name if possible.
- * If we can't, then that is ok.
- */
- outFile = convertName(gunzipConvertTable, outFile);
-
- /*
- * Now build the output path name by prefixing it with
- * the output directory.
- */
- outFile = buildName(outPath, outFile);
-
- /*
- * Uncompress the input file without deleting the input file.
- */
- if (!gunzip(inFile, outFile))
- r = 1;
- }
-
- return r;
-}
-
-
-/*
- * Compress the specified input file to produce the output file.
- * Returns TRUE if successful.
- */
-static BOOL
-gzip(const char * inputFileName, const char * outputFileName)
-{
- gzFile outGZ;
- int inFD;
- int len;
- int err;
- struct stat statBuf1;
- struct stat statBuf2;
- char buf[BUF_SIZE];
-
- outGZ = NULL;
- inFD = -1;
-
- /*
- * See if the output file is the same as the input file.
- * If so, complain about it.
- */
- if (stat(inputFileName, &statBuf1) < 0)
- {
- perror(inputFileName);
-
- return FALSE;
- }
-
- 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))
- {
- fprintf(stderr,
- "Cannot compress file \"%s\" on top of itself\n",
- inputFileName);
-
- return FALSE;
- }
-
- /*
- * Open the input file.
- */
- inFD = open(inputFileName, O_RDONLY);
-
- if (inFD < 0)
- {
- perror(inputFileName);
-
- goto failed;
- }
-
- /*
- * Ask the zlib library to open the output file.
- */
- outGZ = gzopen(outputFileName, "wb9");
-
- if (outGZ == NULL)
- {
- fprintf(stderr, "%s: gzopen failed\n", outputFileName);
-
- goto failed;
- }
-
- /*
- * 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)
- {
- fprintf(stderr, "%s: %s\n", inputFileName,
- gzerror(outGZ, &err));
-
- goto failed;
- }
-
- if (intFlag)
- goto failed;
- }
-
- if (len < 0)
- {
- perror(inputFileName);
-
- goto failed;
- }
-
- /*
- * All done, close the files.
- */
- if (close(inFD))
- {
- perror(inputFileName);
-
- goto failed;
- }
-
- inFD = -1;
-
- if (gzclose(outGZ) != Z_OK)
- {
- fprintf(stderr, "%s: gzclose failed\n", outputFileName);
-
- goto failed;
- }
-
- outGZ = NULL;
-
- /*
- * Success.
- */
- return TRUE;
-
-
-/*
- * Here on an error, to clean up.
- */
-failed:
- if (inFD >= 0)
- (void) close(inFD);
-
- if (outGZ != NULL)
- (void) gzclose(outGZ);
-
- return FALSE;
-}
-
-
-/*
- * Uncompress the input file to produce the output file.
- * Returns TRUE if successful.
- */
-static BOOL
-gunzip(const char * inputFileName, const char * outputFileName)
-{
- gzFile inGZ;
- int outFD;
- int len;
- int err;
- struct stat statBuf1;
- struct stat statBuf2;
- char buf[BUF_SIZE];
-
- inGZ = NULL;
- outFD = -1;
-
- /*
- * See if the output file is the same as the input file.
- * If so, complain about it.
- */
- if (stat(inputFileName, &statBuf1) < 0)
- {
- perror(inputFileName);
-
- return FALSE;
- }
-
- 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))
- {
- fprintf(stderr,
- "Cannot uncompress file \"%s\" on top of itself\n",
- inputFileName);
-
- return FALSE;
- }
-
- /*
- * Ask the zlib library to open the input file.
- */
- inGZ = gzopen(inputFileName, "rb");
-
- if (inGZ == NULL)
- {
- fprintf(stderr, "%s: gzopen failed\n", inputFileName);
-
- return FALSE;
- }
-
- /*
- * Create the output file.
- */
- if (isDevice(outputFileName))
- outFD = open(outputFileName, O_WRONLY);
- else
- outFD = open(outputFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666);
-
- if (outFD < 0)
- {
- perror(outputFileName);
-
- goto failed;
- }
-
- /*
- * 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)
- {
- perror(outputFileName);
-
- goto failed;
- }
-
- if (intFlag)
- goto failed;
- }
-
- if (len < 0)
- {
- fprintf(stderr, "%s: %s\n", inputFileName,
- gzerror(inGZ, &err));
-
- goto failed;
- }
-
- /*
- * All done, close the files.
- */
- if (close(outFD))
- {
- perror(outputFileName);
-
- goto failed;
- }
-
- outFD = -1;
-
- if (gzclose(inGZ) != Z_OK)
- {
- fprintf(stderr, "%s: gzclose failed\n", inputFileName);
-
- goto failed;
- }
-
- inGZ = NULL;
-
- /*
- * Success.
- */
- return TRUE;
-
-
-/*
- * Here on an error, to clean up.
- */
-failed:
- if (outFD >= 0)
- (void) close(outFD);
-
- if (inGZ != NULL)
- (void) gzclose(inGZ);
-
- return FALSE;
-}
-
-
-/*
- * Convert an input file name to an output file name according to
- * the specified convertion table. The returned name points into a
- * static buffer which is overwritten on each call. The table must
- * end in an entry which always specifies a successful conversion.
- * If no conversion is done the original input file name pointer is
- * returned.
- */
-const char *
-convertName(const CONVERT * table, const char * inputFile)
-{
- int inputLength;
- int testLength;
- static char buf[PATH_LEN];
-
- inputLength = strlen(inputFile);
-
- for (;;)
- {
- testLength = strlen(table->input);
-
- if ((inputLength >= testLength) &&
- (memcmp(table->input,
- inputFile + inputLength - testLength,
- testLength) == 0))
- {
- break;
- }
-
- table++;
- }
-
- /*
- * If no conversion was done, return the original pointer.
- */
- if ((testLength == 0) && (table->output[0] == '\0'))
- return inputFile;
-
- /*
- * Build the new name and return it.
- */
- memcpy(buf, inputFile, inputLength - testLength);
-
- memcpy(buf + inputLength - testLength, table->output,
- strlen(table->output) + 1);
-
- return buf;
-}
-
-
-#endif
-
-/* END CODE */
diff --git a/src/sash/cmd_ls.c b/src/sash/cmd_ls.c
deleted file mode 100644
index 41f31cc..0000000
--- a/src/sash/cmd_ls.c
+++ /dev/null
@@ -1,597 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * The "ls" built-in command.
- */
-
-#include "sash.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "dirent.h"
-#include <pwd.h>
-#include <grp.h>
-
-
-#define LISTSIZE 8192
-
-
-#ifdef S_ISLNK
-#define LSTAT lstat
-#else
-#define LSTAT stat
-#endif
-
-
-/*
- * Flags for the LS command.
- */
-#define LSF_LONG 0x01
-#define LSF_DIR 0x02
-#define LSF_INODE 0x04
-#define LSF_MULT 0x08
-#define LSF_FLAG 0x10
-#define LSF_COLUMN 0x20
-#define LSF_NUMERIC 0x40
-
-
-/*
- * Data holding list of files.
- */
-static char ** list;
-static int listSize;
-static int listUsed;
-
-/*
- * Cached user and group name data.
- */
-static char userName[12];
-static int userId;
-static BOOL userIdKnown;
-static char groupName[12];
-static int groupId;
-static BOOL groupIdKnown;
-
-
-/*
- * 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);
-
-
-int
-do_ls(int argc, const char ** argv)
-{
- const char * cp;
- const char * name;
- int flags;
- int i;
- int displayWidth;
- BOOL endSlash;
- DIR * dirp;
- struct dirent * dp;
- char fullName[PATH_LEN];
- struct stat statBuf;
- int r;
-
- static const char * def[] = {"."};
-
- /*
- * Reset for a new listing run.
- */
- clearListNames();
-
- userIdKnown = FALSE;
- groupIdKnown = FALSE;
-
- displayWidth = 0;
- flags = 0;
-
- /*
- * Handle options.
- */
- argc--;
- argv++;
-
- while ((argc > 0) && (**argv == '-'))
- {
- cp = *argv++ + 1;
- argc--;
-
- while (*cp) switch (*cp++)
- {
- case 'l': flags |= LSF_LONG; break;
- case 'n': flags |= LSF_NUMERIC; 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]);
-
- return 1;
- }
- }
-
- /*
- * If long or numeric listing is specified then turn off column listing.
- */
- if (flags & (LSF_LONG | LSF_NUMERIC))
- 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 > 1)
- flags |= LSF_MULT;
-
- /*
- * 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 1;
- }
- }
-
- /*
- * 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 r;
-
- /*
- * 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)
- {
- perror(name);
- r = 1;
-
- continue;
- }
-
- /*
- * If this file name is not a directory, then ignore it.
- */
- if (!S_ISDIR(statBuf.st_mode))
- continue;
-
- /*
- * Collect all the files in the directory.
- */
- dirp = opendir(name);
-
- if (dirp == NULL)
- {
- perror(name);
-
- continue;
- }
-
- if (flags & LSF_MULT)
- printf("\n%s:\n", name);
-
- while (!intFlag && ((dp = readdir(dirp)) != NULL))
- {
- fullName[0] = '\0';
-
- if ((*name != '.') || (name[1] != '\0'))
- {
- strcpy(fullName, name);
-
- if (!endSlash)
- strcat(fullName, "/");
- }
-
- strcat(fullName, dp->d_name);
-
- /*
- * Save the file name in the list.
- */
- if (!addListName(fullName))
- {
- closedir(dirp);
-
- return 1;
- }
- }
-
- closedir(dirp);
-
- /*
- * List the files we collected in this directory,
- * and then clear the list.
- */
- listAllFiles(flags, displayWidth);
- clearListNames();
- }
-
- return r;
-}
-
-
-/*
- * 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++;
-
- if (flags & LSF_INODE)
- fileWidth += 8;
-
- fileWidth += 2;
- }
-
- /*
- * Now list the fileNames.
- */
- for (i = 0; i < listUsed; i++)
- {
- name = list[i];
-
- if (LSTAT(name, &statBuf) < 0)
- {
- perror(name);
-
- continue;
- }
-
- 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 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
-listFile(
- const char * name,
- const struct stat * statBuf,
- int flags,
- int width
-)
-{
- char * cp;
- struct passwd * pwd;
- struct group * grp;
- int len;
- int mode;
- int flagChar;
- int usedWidth;
- char buf[PATH_LEN];
-
- mode = statBuf->st_mode;
-
- /*
- * Initialise buffers for use.
- */
- cp = buf;
- buf[0] = '\0';
- flagChar = '\0';
-
- /*
- * Show the inode number if requested.
- */
- if (flags & LSF_INODE)
- {
- sprintf(cp, "%7ld ", statBuf->st_ino);
- cp += strlen(cp);
- }
-
- /*
- * Create the long or numeric status line if requested.
- */
- if (flags & (LSF_LONG | LSF_NUMERIC))
- {
- strcpy(cp, modeString(mode));
- cp += strlen(cp);
-
- sprintf(cp, "%3ld ", (long) statBuf->st_nlink);
- cp += strlen(cp);
-
- if (!userIdKnown || (statBuf->st_uid != userId))
- {
- if (flags & LSF_NUMERIC)
- pwd = 0;
- else
- pwd = getpwuid(statBuf->st_uid);
-
- if (pwd)
- strcpy(userName, pwd->pw_name);
- else
- sprintf(userName, "%d", statBuf->st_uid);
-
- userId = statBuf->st_uid;
- userIdKnown = TRUE;
- }
-
- sprintf(cp, "%-8s ", userName);
- cp += strlen(cp);
-
- if (!groupIdKnown || (statBuf->st_gid != groupId))
- {
- if (flags & LSF_NUMERIC)
- grp = 0;
- else
- grp = getgrgid(statBuf->st_gid);
-
- if (grp)
- strcpy(groupName, grp->gr_name);
- else
- sprintf(groupName, "%d", statBuf->st_gid);
-
- groupId = statBuf->st_gid;
- groupIdKnown = TRUE;
- }
-
- sprintf(cp, "%-8s ", groupName);
- cp += strlen(cp);
-
- if (S_ISBLK(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);
-
- cp += strlen(cp);
-
- 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(mode))
- {
- len = readlink(name, buf, PATH_LEN - 1);
-
- if (len >= 0)
- {
- buf[len] = '\0';
- printf(" -> %s", buf);
- }
-
- usedWidth += strlen(buf) + 4;
- }
-#endif
-
- /*
- * 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 */
diff --git a/src/sash/cmd_tar.c b/src/sash/cmd_tar.c
deleted file mode 100644
index 5dd5f21..0000000
--- a/src/sash/cmd_tar.c
+++ /dev/null
@@ -1,1277 +0,0 @@
-/*
- * Copyright (c) 2014 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 constants.
- */
-#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 BOOL readTarFile(int fileCount, const char ** fileTable);
-static BOOL readData(const char * cp, int count);
-static BOOL createPath(const char * name, int mode);
-static long getOctal(const char * cp, int len);
-
-static BOOL 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 BOOL writeTarFile(int fileCount, const char ** fileTable);
-static void writeTarBlock(const char * buf, int len);
-static BOOL putOctal(char * cp, int len, long value);
-
-
-
-int
-do_tar(int argc, const char ** argv)
-{
- const char * options;
- BOOL successFlag;
-
- argc--;
- argv++;
-
- if (argc < 2)
- {
- fprintf(stderr, "Too few arguments for tar\n");
-
- return 1;
- }
-
- 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 1;
- }
-
- tarName = *argv++;
- argc--;
-
- break;
-
- case 't':
- listFlag = TRUE;
- break;
-
- case 'x':
- extractFlag = TRUE;
- break;
-
- case 'c':
- createFlag = TRUE;
- break;
-
- case 'v':
- verboseFlag = TRUE;
- break;
-
- default:
- fprintf(stderr, "Unknown tar flag '%c'\n", *options);
-
- return 1;
- }
- }
-
- /*
- * Validate the options.
- */
- if (extractFlag + listFlag + createFlag != 1)
- {
- fprintf(stderr, "Exactly one of 'c', 'x' or 't' must be specified\n");
-
- return 1;
- }
-
- if (tarName == NULL)
- {
- fprintf(stderr, "The 'f' flag must be specified\n");
-
- return 1;
- }
-
- /*
- * Do the correct type of action supplying the rest of the
- * command line arguments as the list of files to process.
- */
- if (createFlag)
- successFlag = writeTarFile(argc, argv);
- else
- successFlag = readTarFile(argc, argv);
-
- return !successFlag;
-}
-
-
-/*
- * 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.
- * Returns TRUE on success.
- */
-static BOOL
-readTarFile(int fileCount, const char ** fileTable)
-{
- const char * cp;
- BOOL successFlag;
- int cc;
- int inCc;
- int blockSize;
- char buf[BUF_SIZE];
-
- skipFileFlag = FALSE;
- badHeader = FALSE;
- warnedRoot = FALSE;
- eofFlag = FALSE;
- inHeader = TRUE;
- successFlag = TRUE;
-
- inCc = 0;
- dataCc = 0;
- outFd = -1;
- blockSize = sizeof(buf);
- cp = buf;
-
- /*
- * Open the tar file for reading.
- */
- tarFd = open(tarName, O_RDONLY);
-
- if (tarFd < 0)
- {
- perror(tarName);
-
- return FALSE;
- }
-
- /*
- * 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;
- inCc = fullRead(tarFd, buf, blockSize);
-
- if (inCc < 0)
- {
- perror(tarName);
- successFlag = FALSE;
-
- goto done;
- }
-
- if (inCc == 0)
- {
- fprintf(stderr,
- "Unexpected end of file from \"%s\"",
- tarName);
- successFlag = FALSE;
-
- goto done;
- }
- }
-
- /*
- * If we are expecting a header block then examine it.
- */
- if (inHeader)
- {
- if (!readHeader((const TarHeader *) cp, fileCount, fileTable))
- successFlag = FALSE;
-
- cp += TAR_BLOCK_SIZE;
- inCc -= TAR_BLOCK_SIZE;
-
- continue;
- }
-
- /*
- * 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 (!readData(cp, cc))
- successFlag = FALSE;
-
- /*
- * 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;
- }
-
- /*
- * Check for an interrupt.
- */
- if (intFlag)
- {
- fprintf(stderr, "Interrupted - aborting\n");
- successFlag = FALSE;
- }
-
-done:
- /*
- * Close the tar file if needed.
- */
- if ((tarFd >= 0) && (close(tarFd) < 0))
- {
- perror(tarName);
- successFlag = FALSE;
- }
-
- /*
- * 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);
-
- return successFlag;
-}
-
-
-/*
- * 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. Returns TRUE on success.
- */
-static BOOL
-readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
-{
- int mode;
- int uid;
- int gid;
- long size;
- time_t mtime;
- const char * name;
- int cc;
- BOOL hardLink;
- BOOL softLink;
-
- /*
- * If the block is completely empty, then this is the end of the
- * archive file. If the name is null, then just skip this header.
- */
- name = hp->name;
-
- if (*name == '\0')
- {
- for (cc = TAR_BLOCK_SIZE; cc > 0; cc--)
- {
- if (*name++)
- return TRUE;
- }
-
- eofFlag = TRUE;
-
- return TRUE;
- }
-
- /*
- * 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));
-
- if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0))
- {
- if (!badHeader)
- fprintf(stderr, "Bad tar header, skipping\n");
-
- badHeader = TRUE;
-
- return FALSE;
- }
-
- badHeader = FALSE;
- skipFileFlag = FALSE;
-
- /*
- * Check for the file modes.
- */
- hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) ||
- (hp->typeFlag == TAR_TYPE_HARD_LINK - '0'));
-
- 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;
-
- /*
- * 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 path detected, removing leading slashes\n");
- }
-
- warnedRoot = TRUE;
- }
-
- /*
- * 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 TRUE;
- }
-
- /*
- * 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;
- }
-
- printf("\n");
-
- return TRUE;
- }
-
- /*
- * We really want to extract the file.
- */
- if (verboseFlag)
- printf("x %s\n", name);
-
- if (hardLink)
- {
- if (link(hp->linkName, name) < 0)
- {
- perror(name);
- return FALSE;
- }
-
- return TRUE;
- }
-
- if (softLink)
- {
-#ifdef S_ISLNK
- if (symlink(hp->linkName, name) < 0)
- {
- perror(name);
-
- return FALSE;
- }
-
- return TRUE;
-#else
- fprintf(stderr, "Cannot create symbolic links\n");
-#endif
- return FALSE;
- }
-
- /*
- * If the file is a directory, then just create the path.
- */
- if (S_ISDIR(mode))
- return createPath(name, mode);
-
- /*
- * There is a file to write.
- * First create the path to it if necessary with a default permission.
- */
- if (!createPath(name, 0777))
- return FALSE;
-
- inHeader = (size == 0);
- dataCc = size;
-
- /*
- * Start the output file.
- */
- outFd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode);
-
- if (outFd < 0)
- {
- perror(name);
- skipFileFlag = TRUE;
-
- return FALSE;
- }
-
- /*
- * If the file is empty, then that's all we need to do.
- */
- if (size == 0)
- {
- (void) close(outFd);
- outFd = -1;
- }
-
- return TRUE;
-}
-
-
-/*
- * Handle a data block of some specified size that was read.
- * Returns TRUE on success.
- */
-static BOOL
-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 TRUE;
-
- /*
- * Write the data to the output file.
- */
- if (fullWrite(outFd, cp, count) < 0)
- {
- perror(outName);
- (void) close(outFd);
- outFd = -1;
- skipFileFlag = TRUE;
-
- return FALSE;
- }
-
- /*
- * If the write failed, close the file and disable further
- * writes to this file.
- */
- if (dataCc <= 0)
- {
- if (close(outFd))
- perror(outName);
-
- outFd = -1;
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-/*
- * Write a tar file containing the specified files.
- * Returns TRUE on success.
- */
-static BOOL
-writeTarFile(int fileCount, const char ** fileTable)
-{
- struct stat statbuf;
- BOOL successFlag;
-
- successFlag = TRUE;
- errorFlag = FALSE;
-
- /*
- * Make sure there is at least one file specified.
- */
- if (fileCount <= 0)
- {
- fprintf(stderr, "No files specified to be saved\n");
-
- return FALSE;
- }
-
- /*
- * Create the tar file for writing.
- */
- tarFd = open(tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-
- if (tarFd < 0)
- {
- perror(tarName);
-
- return FALSE;
- }
-
- /*
- * Get the device and inode of the tar file for checking later.
- */
- if (fstat(tarFd, &statbuf) < 0)
- {
- perror(tarName);
- successFlag = FALSE;
-
- goto done;
- }
-
- 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");
- successFlag = FALSE;
- }
-
- /*
- * 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);
- successFlag = FALSE;
- }
-
- return successFlag;
-}
-
-
-/*
- * 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;
- }
-}
-
-
-/*
- * Attempt to create the directories along the specified path, except for
- * the final component. The mode is given for the final directory only,
- * while all previous ones get default protections. Errors are not reported
- * here, as failures to restore files can be reported later.
- * Returns TRUE on success.
- */
-static BOOL
-createPath(const char * name, int mode)
-{
- char * cp;
- char * cpOld;
- char buf[TAR_NAME_SIZE];
-
- strcpy(buf, name);
-
- cp = strchr(buf, '/');
-
- while (cp)
- {
- cpOld = cp;
- cp = strchr(cp + 1, '/');
-
- *cpOld = '\0';
-
- if (mkdir(buf, cp ? 0777 : mode) == 0)
- printf("Directory \"%s\" created\n", buf);
-
- *cpOld = '/';
- }
-
- return TRUE;
-}
-
-
-/*
- * Read an octal value in a field of the specified width, with optional
- * spaces on both sides of the number and with an optional null character
- * at the end. Returns -1 on an illegal format.
- */
-static long
-getOctal(const char * cp, int len)
-{
- long val;
-
- while ((len > 0) && (*cp == ' '))
- {
- cp++;
- len--;
- }
-
- if ((len == 0) || !isOctal(*cp))
- return -1;
-
- val = 0;
-
- while ((len > 0) && isOctal(*cp))
- {
- val = val * 8 + *cp++ - '0';
- len--;
- }
-
- while ((len > 0) && (*cp == ' '))
- {
- cp++;
- len--;
- }
-
- if ((len > 0) && *cp)
- return -1;
-
- 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/src/sash/cmds.c b/src/sash/cmds.c
deleted file mode 100644
index ae346b5..0000000
--- a/src/sash/cmds.c
+++ /dev/null
@@ -1,1562 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Most simple built-in commands are here.
- */
-
-#include "sash.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-//#include <sys/mount.h>
-#include <signal.h>
-#include <pwd.h>
-#include <grp.h>
-#include <utime.h>
-#include <errno.h>
-
-#if HAVE_LINUX_MOUNT
-#include <linux/fs.h>
-#endif
-
-/* Need to tell loop.h what the actual dev_t type is. */
-#undef dev_t
-#if defined(__alpha) || (defined(__sparc__) && defined(__arch64__))
-#define dev_t unsigned int
-#else
-#define dev_t unsigned short
-#endif
-//#include <linux/loop.h>
-#undef dev_t
-#define dev_t dev_t
-
-
-int
-do_echo(int argc, const char ** argv)
-{
- BOOL first;
-
- first = TRUE;
-
- while (argc-- > 1)
- {
- if (!first)
- fputc(' ', stdout);
-
- first = FALSE;
- fputs(*(++argv), stdout);
- }
-
- fputc('\n', stdout);
-
- return 0;
-}
-
-
-int
-do_pwd(int argc, const char ** argv)
-{
- char buf[PATH_LEN];
-
- if (getcwd(buf, PATH_LEN) == NULL)
- {
- fprintf(stderr, "Cannot get current directory\n");
-
- return 1;
- }
-
- printf("%s\n", buf);
-
- return 0;
-}
-
-
-int
-do_cd(int argc, const char ** argv)
-{
- const char * path;
-
- if (argc > 1)
- path = argv[1];
- else
- {
- path = getenv("HOME");
-
- if (path == NULL)
- {
- fprintf(stderr, "No HOME environment variable\n");
-
- return 1;
- }
- }
-
- if (chdir(path) < 0)
- {
- perror(path);
-
- return 1;
- }
-
- return 0;
-}
-
-
-int
-do_mkdir(int argc, const char ** argv)
-{
- int r = 0;
-
- while (argc-- > 1)
- {
- if (mkdir(argv[1], 0777) < 0)
- {
- perror(argv[1]);
- r = 1;
- }
-
- argv++;
- }
-
- return r;
-}
-
-
-int
-do_mknod(int argc, const char ** argv)
-{
- const char * cp;
- int mode;
- int major;
- int minor;
-
- mode = 0666;
-
- if (strcmp(argv[2], "b") == 0)
- mode |= S_IFBLK;
- else if (strcmp(argv[2], "c") == 0)
- mode |= S_IFCHR;
- else
- {
- fprintf(stderr, "Bad device type\n");
-
- return 1;
- }
-
- major = 0;
- cp = argv[3];
-
- while (isDecimal(*cp))
- major = major * 10 + *cp++ - '0';
-
- if (*cp || (major < 0) || (major > 255))
- {
- fprintf(stderr, "Bad major number\n");
-
- return 1;
- }
-
- minor = 0;
- cp = argv[4];
-
- while (isDecimal(*cp))
- minor = minor * 10 + *cp++ - '0';
-
- if (*cp || (minor < 0) || (minor > 255))
- {
- fprintf(stderr, "Bad minor number\n");
-
- return 1;
- }
-
- if (mknod(argv[1], mode, major * 256 + minor) < 0)
- {
- perror(argv[1]);
-
- return 1;
- }
-
- return 0;
-}
-
-
-#if HAVE_LINUX_PIVOT
-
-int
-do_pivot_root(int argc, const char ** argv)
-{
- if (pivot_root(argv[1], argv[2]) < 0)
- {
- perror("pivot_root");
-
- return 1;
- }
-
- return 0;
-}
-
-#endif
-
-
-#if HAVE_LINUX_CHROOT
-
-int
-do_chroot(int argc, const char ** argv)
-{
- if (chroot(argv[1]) < 0)
- {
- perror("chroot");
-
- return 1;
- }
-
- return 0;
-}
-
-#endif
-
-
-int
-do_rmdir(int argc, const char ** argv)
-{
- int r = 0;
-
- while (argc-- > 1)
- {
- if (rmdir(argv[1]) < 0)
- {
- perror(argv[1]);
- r = 1;
- }
-
- argv++;
- }
-
- return r;
-}
-
-
-int
-do_sync(int argc, const char ** argv)
-{
- sync();
-
- return 0;
-}
-
-
-int
-do_rm(int argc, const char ** argv)
-{
- int r = 0;
-
- while (argc-- > 1)
- {
- if (unlink(argv[1]) < 0)
- {
- perror(argv[1]);
- r = 1;
- }
-
- argv++;
- }
-
- return r;
-}
-
-
-int
-do_chmod(int argc, const char ** argv)
-{
- const char * cp;
- int mode;
- int r;
-
- r = 0;
- mode = 0;
- cp = argv[1];
-
- while (isOctal(*cp))
- mode = mode * 8 + (*cp++ - '0');
-
- if (*cp)
- {
- fprintf(stderr, "Mode must be octal\n");
-
- return 1;
- }
-
- argc--;
- argv++;
-
- while (argc-- > 1)
- {
- if (chmod(argv[1], mode) < 0)
- {
- perror(argv[1]);
- r = 1;
- }
-
- argv++;
- }
-
- return r;
-}
-
-
-int
-do_chown(int argc, const char ** argv)
-{
- const char * cp;
- int uid;
- struct passwd * pwd;
- struct stat statBuf;
- int r;
-
- r = 0;
- cp = argv[1];
-
- if (isDecimal(*cp))
- {
- uid = 0;
-
- while (isDecimal(*cp))
- uid = uid * 10 + (*cp++ - '0');
-
- if (*cp)
- {
- fprintf(stderr, "Bad uid value\n");
-
- return 1;
- }
- }
- else
- {
- pwd = getpwnam(cp);
-
- if (pwd == NULL)
- {
- fprintf(stderr, "Unknown user name\n");
-
- return 1;
- }
-
- uid = pwd->pw_uid;
- }
-
- argc--;
- argv++;
-
- while (argc-- > 1)
- {
- argv++;
-
- if ((stat(*argv, &statBuf) < 0) ||
- (chown(*argv, uid, statBuf.st_gid) < 0))
- {
- perror(*argv);
- r = 1;
- }
- }
-
- return r;
-}
-
-
-int
-do_chgrp(int argc, const char ** argv)
-{
- const char * cp;
- int gid;
- struct group * grp;
- struct stat statBuf;
- int r;
-
- r = 0;
- cp = argv[1];
-
- if (isDecimal(*cp))
- {
- gid = 0;
-
- while (isDecimal(*cp))
- gid = gid * 10 + (*cp++ - '0');
-
- if (*cp)
- {
- fprintf(stderr, "Bad gid value\n");
-
- return 1;
- }
- }
- else
- {
- grp = getgrnam(cp);
-
- if (grp == NULL)
- {
- fprintf(stderr, "Unknown group name\n");
-
- return 1;
- }
-
- gid = grp->gr_gid;
- }
-
- argc--;
- argv++;
-
- while (argc-- > 1)
- {
- argv++;
-
- if ((stat(*argv, &statBuf) < 0) ||
- (chown(*argv, statBuf.st_uid, gid) < 0))
- {
- perror(*argv);
- r = 1;
- }
- }
-
- return r;
-}
-
-
-int
-do_touch(int argc, const char ** argv)
-{
- const char * name;
- int fd;
- struct utimbuf now;
- int r;
-
- r = 0;
- time(&now.actime);
- now.modtime = now.actime;
-
- while (argc-- > 1)
- {
- name = *(++argv);
-
- fd = open(name, O_CREAT | O_WRONLY | O_EXCL, 0666);
-
- if (fd >= 0)
- {
- close(fd);
-
- continue;
- }
-
- if (errno != EEXIST)
- {
- perror(name);
- r = 1;
-
- continue;
- }
-
- if (utime(name, &now) < 0)
- {
- perror(name);
- r = 1;
- }
- }
-
- return r;
-}
-
-
-int
-do_mv(int argc, const char ** argv)
-{
- const char * srcName;
- const char * destName;
- const char * lastArg;
- BOOL dirFlag;
- int r;
-
- r = 0;
- lastArg = argv[argc - 1];
-
- dirFlag = isDirectory(lastArg);
-
- if ((argc > 3) && !dirFlag)
- {
- fprintf(stderr, "%s: not a directory\n", lastArg);
-
- return 1;
- }
-
- while (!intFlag && (argc-- > 2))
- {
- srcName = *(++argv);
-
- if (access(srcName, 0) < 0)
- {
- perror(srcName);
- r = 1;
-
- continue;
- }
-
- destName = lastArg;
-
- if (dirFlag)
- destName = buildName(destName, srcName);
-
- if (rename(srcName, destName) >= 0)
- continue;
-
- if (errno != EXDEV)
- {
- perror(destName);
- r = 1;
-
- continue;
- }
-
- if (!copyFile(srcName, destName, TRUE))
- {
- r = 1;
-
- continue;
- }
-
- if (unlink(srcName) < 0)
- {
- perror(srcName);
- r = 1;
- }
- }
-
- return r;
-}
-
-
-int
-do_ln(int argc, const char ** argv)
-{
- const char * srcName;
- const char * destName;
- const char * lastArg;
- BOOL dirFlag;
- int r;
-
- r = 0;
-
- if (argv[1][0] == '-')
- {
- if (strcmp(argv[1], "-s"))
- {
- fprintf(stderr, "Unknown option\n");
-
- return 1;
- }
-
- if (argc != 4)
- {
- fprintf(stderr, "Wrong number of arguments for symbolic link\n");
-
- return 1;
- }
-
-#ifdef S_ISLNK
- if (symlink(argv[2], argv[3]) < 0)
- {
- perror(argv[3]);
-
- return 1;
- }
-
- return 0;
-#else
- fprintf(stderr, "Symbolic links are not allowed\n");
-
- return 1;
-#endif
- }
-
- /*
- * Here for normal hard links.
- */
- lastArg = argv[argc - 1];
- dirFlag = isDirectory(lastArg);
-
- if ((argc > 3) && !dirFlag)
- {
- fprintf(stderr, "%s: not a directory\n", lastArg);
-
- return 1;
- }
-
- while (argc-- > 2)
- {
- srcName = *(++argv);
-
- if (access(srcName, 0) < 0)
- {
- perror(srcName);
- r = 1;
-
- continue;
- }
-
- destName = lastArg;
-
- if (dirFlag)
- destName = buildName(destName, srcName);
-
- if (link(srcName, destName) < 0)
- {
- perror(destName);
- r = 1;
-
- continue;
- }
- }
-
- return r;
-}
-
-
-int
-do_cp(int argc, const char ** argv)
-{
- const char * srcName;
- const char * destName;
- const char * lastArg;
- BOOL dirFlag;
- int r;
-
- r = 0;
- lastArg = argv[argc - 1];
-
- dirFlag = isDirectory(lastArg);
-
- if ((argc > 3) && !dirFlag)
- {
- fprintf(stderr, "%s: not a directory\n", lastArg);
-
- return 1;
- }
-
- while (!intFlag && (argc-- > 2))
- {
- srcName = *(++argv);
- destName = lastArg;
-
- if (dirFlag)
- destName = buildName(destName, srcName);
-
- if (!copyFile(srcName, destName, FALSE))
- r = 1;
- }
-
- return r;
-}
-
-
-int
-do_mount(int argc, const char ** argv)
-{
- const char * str;
- const char * type;
- int flags;
-
- argc--;
- argv++;
-
- type = MOUNT_TYPE;
-
-#if HAVE_LINUX_MOUNT
- flags = MS_MGC_VAL;
-#else
- flags = 0;
-#endif
-
- while ((argc > 0) && (**argv == '-'))
- {
- argc--;
- str = *argv++;
-
- while (*++str) switch (*str)
- {
- case 't':
- if ((argc <= 0) || (**argv == '-'))
- {
- fprintf(stderr, "Missing file system type\n");
-
- return 1;
- }
-
- type = *argv++;
- argc--;
- break;
-
-#if HAVE_LINUX_MOUNT
- case 'r':
- flags |= MS_RDONLY;
- break;
-
- case 'm':
- flags |= MS_REMOUNT;
- break;
-
- case 's':
- flags |= MS_NOSUID;
- break;
-
- case 'e':
- flags |= MS_NOEXEC;
- break;
-
-#elif HAVE_BSD_MOUNT
- case 'r':
- flags |= MNT_RDONLY;
- break;
-
- case 's':
- flags |= MNT_NOSUID;
- break;
-
- case 'e':
- flags |= MNT_NOEXEC;
- break;
-#endif
- default:
- fprintf(stderr, "Unknown option\n");
-
- return 1;
- }
- }
-
- if (argc != 2)
- {
- fprintf(stderr, "Wrong number of arguments for mount\n");
-
- return 1;
- }
-
-#if HAVE_LINUX_MOUNT
-
- if (mount(argv[0], argv[1], type, flags, 0) < 0)
- {
- perror("mount failed");
- return 1;
- }
-#elif HAVE_BSD_MOUNT
- {
- struct ufs_args ufs;
- struct adosfs_args adosfs;
- struct iso_args iso;
- struct mfs_args mfs;
- struct msdosfs_args msdosfs;
- void * args;
-
- if (!strcmp(type, "ffs") || !strcmp(type, "ufs"))
- {
- ufs.fspec = (char*) argv[0];
- args = &ufs;
- }
- else if (!strcmp(type, "adosfs"))
- {
- adosfs.fspec = (char*) argv[0];
- adosfs.uid = 0;
- adosfs.gid = 0;
- args = &adosfs;
- }
- else if (!strcmp(type, "cd9660"))
- {
- iso.fspec = (char*) argv[0];
- args = &iso;
- }
- else if (!strcmp(type, "mfs"))
- {
- mfs.fspec = (char*) argv[0];
- args = &mfs;
- }
- else if (!strcmp(type, "msdos"))
- {
- msdosfs.fspec = (char*) argv[0];
- msdosfs.uid = 0;
- msdosfs.gid = 0;
- args = &msdosfs;
- }
- else
- {
- fprintf(stderr, "Unknown filesystem type: %s", type);
- fprintf(stderr,
- "Supported: ffs ufs adosfs cd9660 mfs msdos\n");
-
- return 1;
- }
-
- if (mount(type, argv[1], flags, args) < 0)
- {
- perror(argv[0]);
-
- return 1;
- }
- }
-#endif
- return 0;
-}
-
-
-int
-do_umount(int argc, const char ** argv)
-{
-#if HAVE_LINUX_MOUNT
- if (umount(argv[1]) < 0)
- {
- perror(argv[1]);
-
- return 1;
- }
-#elif HAVE_BSD_MOUNT
- {
- const char * str;
- int flags = 0;
-
- for (argc--, argv++;
- (argc > 0) && (**argv == '-');)
- {
- argc--;
- str = *argv++;
-
- while (*++str)
-{
- switch (*str)
- {
- case 'f':
- flags = MNT_FORCE;
- break;
- }
- }
- }
-
- if (unmount(argv[0], flags) < 0)
- {
- perror(argv[0]);
-
- return 1;
- }
- }
-#endif
- return 0;
-}
-
-
-int
-do_cmp(int argc, const char ** argv)
-{
- int fd1;
- int fd2;
- int cc1;
- int cc2;
- long pos;
- const char * bp1;
- const char * bp2;
- char buf1[BUF_SIZE];
- char buf2[BUF_SIZE];
- struct stat statBuf1;
- struct stat statBuf2;
- int r;
-
- r = 0;
-
- if (stat(argv[1], &statBuf1) < 0)
- {
- perror(argv[1]);
-
- return 1;
- }
-
- if (stat(argv[2], &statBuf2) < 0)
- {
- perror(argv[2]);
-
- return 1;
- }
-
- if ((statBuf1.st_dev == statBuf2.st_dev) &&
- (statBuf1.st_ino == statBuf2.st_ino))
- {
- printf("Files are links to each other\n");
-
- return 0;
- }
-
- if (statBuf1.st_size != statBuf2.st_size)
- {
- printf("Files are different sizes\n");
-
- return 1;
- }
-
- fd1 = open(argv[1], O_RDONLY);
-
- if (fd1 < 0)
- {
- perror(argv[1]);
-
- return 1;
- }
-
- fd2 = open(argv[2], O_RDONLY);
-
- if (fd2 < 0)
- {
- perror(argv[2]);
- close(fd1);
-
- return 1;
- }
-
- pos = 0;
-
- while (TRUE)
- {
- if (intFlag)
- goto closefiles;
-
- cc1 = read(fd1, buf1, sizeof(buf1));
-
- if (cc1 < 0)
- {
- perror(argv[1]);
- r = 1;
- goto closefiles;
- }
-
- cc2 = read(fd2, buf2, sizeof(buf2));
-
- if (cc2 < 0)
- {
- perror(argv[2]);
- r = 1;
- goto closefiles;
- }
-
- if ((cc1 == 0) && (cc2 == 0))
- {
- printf("Files are identical\n");
- r = 0;
- goto closefiles;
- }
-
- if (cc1 < cc2)
- {
- printf("First file is shorter than second\n");
- r = 1;
- goto closefiles;
- }
-
- if (cc1 > cc2)
- {
- printf("Second file is shorter than first\n");
- r = 1;
- goto closefiles;
- }
-
- if (memcmp(buf1, buf2, cc1) == 0)
- {
- pos += cc1;
-
- continue;
- }
-
- bp1 = buf1;
- bp2 = buf2;
-
- while (*bp1++ == *bp2++)
- pos++;
-
- printf("Files differ at byte position %ld\n", pos);
- r = 1;
-
- goto closefiles;
- }
-
-closefiles:
- close(fd1);
- close(fd2);
-
- return r;
-}
-
-
-int
-do_more(int argc, const char ** argv)
-{
- FILE * fp;
- const char * name;
- int ch;
- int line;
- int col;
- int pageLines;
- int pageColumns;
- char buf[80];
-
- /*
- * Get the width and height of the screen if it is set.
- * If not, then default it.
- */
- pageLines = 0;
- pageColumns = 0;
-
- name = getenv("LINES");
-
- if (name)
- pageLines = atoi(name);
-
- name = getenv("COLS");
-
- if (name)
- pageColumns = atoi(name);
-
- if (pageLines <= 0)
- pageLines = 24;
-
- if (pageColumns <= 0)
- pageColumns = 80;
-
- /*
- * OK, process each file.
- */
- while (argc-- > 1)
- {
- name = *(++argv);
-
- fp = fopen(name, "r");
-
- if (fp == NULL)
- {
- perror(name);
-
- return 1;
- }
-
- printf("<< %s >>\n", name);
- line = 1;
- col = 0;
-
- while (fp && ((ch = fgetc(fp)) != EOF))
- {
- switch (ch)
- {
- case '\r':
- col = 0;
- break;
-
- case '\n':
- line++;
- col = 0;
- break;
-
- case '\t':
- col = ((col + 1) | 0x07) + 1;
- break;
-
- case '\b':
- if (col > 0)
- col--;
- break;
-
- default:
- col++;
- }
-
- putchar(ch);
-
- if (col >= pageColumns)
- {
- col -= pageColumns;
- line++;
- }
-
- if (line < pageLines)
- continue;
-
- if (col > 0)
- putchar('\n');
-
- printf("--More--");
- fflush(stdout);
-
- if (intFlag || (read(0, buf, sizeof(buf)) < 0))
- {
- if (fp)
- fclose(fp);
-
- return 0;
- }
-
- ch = buf[0];
-
- if (ch == ':')
- ch = buf[1];
-
- switch (ch)
- {
- case 'N':
- case 'n':
- fclose(fp);
- fp = NULL;
- break;
-
- case 'Q':
- case 'q':
- fclose(fp);
-
- return 0;
- }
-
- col = 0;
- line = 1;
- }
-
- if (fp)
- fclose(fp);
- }
-
- return 0;
-}
-
-
-int
-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];
- int r;
-
- argc--;
- argv++;
- r = 0;
-
- while (argc-- > 0)
- {
- name = *argv++;
-
- fd = open(name, O_RDONLY);
-
- if (fd < 0)
- {
- perror(name);
- r = 1;
-
- 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);
- r = 1;
-
- (void) close(fd);
-
- continue;
- }
-
- (void) close(fd);
-
- printf("%05lu %s\n", checksum, name);
- }
-
- return r;
-}
-
-
-int
-do_exit(int argc, const char ** argv)
-{
- int r = 0;
-
- if (getpid() == 1)
- {
- fprintf(stderr, "You are the INIT process!\n");
-
- return 1;
- }
-
- if (argc == 2)
- {
- r = atoi(argv[1]);
- }
-
- exit(r);
-
- return 1;
-}
-
-
-int
-do_setenv(int argc, const char ** argv)
-{
- const char * name;
- const char * value;
- char * str;
-
- name = argv[1];
- value = argv[2];
-
- /*
- * The value given to putenv must remain around, so we must malloc it.
- * Note: memory is not reclaimed if the same variable is redefined.
- */
- str = malloc(strlen(name) + strlen(value) + 2);
-
- if (str == NULL)
- {
- fprintf(stderr, "Cannot allocate memory\n");
-
- return 1;
- }
-
- strcpy(str, name);
- strcat(str, "=");
- strcat(str, value);
-
- putenv(str);
-
- return 0;
-}
-
-
-int
-do_printenv(int argc, const char ** argv)
-{
- const char ** env;
- extern char ** environ;
- int len;
-
- env = (const char **) environ;
-
- if (argc == 1)
- {
- while (*env)
- printf("%s\n", *env++);
-
- return 0;
- }
-
- len = strlen(argv[1]);
-
- while (*env)
- {
- if ((strlen(*env) > len) && (env[0][len] == '=') &&
- (memcmp(argv[1], *env, len) == 0))
- {
- printf("%s\n", &env[0][len+1]);
-
- return 0;
- }
- env++;
- }
-
- return 0;
-}
-
-
-int
-do_umask(int argc, const char ** argv)
-{
- const char * cp;
- int mask;
-
- if (argc <= 1)
- {
- mask = umask(0);
- umask(mask);
- printf("%03o\n", mask);
-
- return 0;
- }
-
- mask = 0;
- cp = argv[1];
-
- while (isOctal(*cp))
- mask = mask * 8 + *cp++ - '0';
-
- if (*cp || (mask & ~0777))
- {
- fprintf(stderr, "Bad umask value\n");
-
- return 1;
- }
-
- umask(mask);
-
- return 0;
-}
-
-
-int
-do_kill(int argc, const char ** argv)
-{
- const char * cp;
- int sig;
- int pid;
- int r;
-
- r = 0;
- sig = SIGTERM;
-
- if (argv[1][0] == '-')
- {
- cp = &argv[1][1];
-
- if (strcmp(cp, "HUP") == 0)
- sig = SIGHUP;
- else if (strcmp(cp, "INT") == 0)
- sig = SIGINT;
- else if (strcmp(cp, "QUIT") == 0)
- sig = SIGQUIT;
- else if (strcmp(cp, "KILL") == 0)
- sig = SIGKILL;
- else if (strcmp(cp, "STOP") == 0)
- sig = SIGSTOP;
- else if (strcmp(cp, "CONT") == 0)
- sig = SIGCONT;
- else if (strcmp(cp, "USR1") == 0)
- sig = SIGUSR1;
- else if (strcmp(cp, "USR2") == 0)
- sig = SIGUSR2;
- else if (strcmp(cp, "TERM") == 0)
- sig = SIGTERM;
- else
- {
- sig = 0;
-
- while (isDecimal(*cp))
- sig = sig * 10 + *cp++ - '0';
-
- if (*cp)
- {
- fprintf(stderr, "Unknown signal\n");
-
- return 1;
- }
- }
-
- argc--;
- argv++;
- }
-
- while (argc-- > 1)
- {
- cp = *(++argv);
- pid = 0;
-
- while (isDecimal(*cp))
- pid = pid * 10 + *cp++ - '0';
-
- if (*cp)
- {
- fprintf(stderr, "Non-numeric pid\n");
-
- return 1;
- }
-
- if (kill(pid, sig) < 0)
- {
- perror(*argv);
- r = 1;
- }
- }
-
- return r;
-}
-
-
-int
-do_where(int argc, const char ** argv)
-{
- const char * program;
- const char * dirName;
- char * path;
- char * endPath;
- char * fullPath;
- BOOL found;
- int r;
-
- found = FALSE;
- program = argv[1];
-
- if (strchr(program, '/') != NULL)
- {
- fprintf(stderr, "Program name cannot include a path\n");
-
- return 1;
- }
-
- path = getenv("PATH");
-
- fullPath = getChunk(strlen(path) + strlen(program) + 2);
- path = chunkstrdup(path);
-
- if ((path == NULL) || (fullPath == NULL))
- {
- fprintf(stderr, "Memory allocation failed\n");
-
- return 1;
- }
-
- /*
- * Check out each path to see if the program exists and is
- * executable in that path.
- */
- for (; path; path = endPath)
- {
- /*
- * Find the end of the next path and NULL terminate
- * it if necessary.
- */
- endPath = strchr(path, ':');
-
- if (endPath)
- *endPath++ = '\0';
-
- /*
- * Get the directory name, defaulting it to DOT if
- * it is null.
- */
- dirName = path;
-
- if (dirName == '\0')
- dirName = ".";
-
- /*
- * Construct the full path of the program.
- */
- strcpy(fullPath, dirName);
- strcat(fullPath, "/");
- strcat(fullPath, program);
-
- /*
- * See if the program exists and is executable.
- */
- if (access(fullPath, X_OK) < 0)
- {
- if (errno != ENOENT)
- {
- perror(fullPath);
- r = 1;
- }
-
- continue;
- }
-
- printf("%s\n", fullPath);
- found = TRUE;
- }
-
- if (!found)
- {
- printf("Program \"%s\" not found in PATH\n", program);
- r = 1;
- }
-
- return r;
-}
-
-#if HAVE_LINUX_LOSETUP
-
-int
-do_losetup(int argc, const char ** argv)
-{
- int loopfd;
- int targfd;
- struct loop_info loopInfo;
-
- if (!strcmp(argv[1], "-d"))
- {
- loopfd = open(argv[2], O_RDWR);
-
- if (loopfd < 0)
- {
- fprintf(stderr, "Error opening %s: %s\n", argv[2],
- strerror(errno));
-
- return 1;
- }
-
- if (ioctl(loopfd, LOOP_CLR_FD, 0))
- {
- fprintf(stderr, "Error unassociating device: %s\n",
- strerror(errno));
-
- return 1;
- }
- }
-
- loopfd = open(argv[1], O_RDWR);
-
- if (loopfd < 0)
- {
- fprintf(stderr, "Error opening %s: %s\n", argv[1],
- strerror(errno));
-
- return 1;
- }
-
- targfd = open(argv[2], O_RDWR);
-
- if (targfd < 0)
- {
- fprintf(stderr, "Error opening %s: %s\n", argv[2],
- strerror(errno));
-
- return 1;
- }
-
- if (ioctl(loopfd, LOOP_SET_FD, targfd))
- {
- fprintf(stderr, "Error setting up loopback device: %s\n",
- strerror(errno));
-
- return 1;
- }
-
- memset(&loopInfo, 0, sizeof(loopInfo));
- strcpy(loopInfo.lo_name, argv[2]);
-
- if (ioctl(loopfd, LOOP_SET_STATUS, &loopInfo))
- {
- fprintf(stderr, "Error setting up loopback device: %s\n",
- strerror(errno));
-
- return 1;
- }
-
- return 0;
-}
-
-#endif
-
-/* END CODE */
diff --git a/src/sash/dirent.h b/src/sash/dirent.h
deleted file mode 100644
index 77db3b4..0000000
--- a/src/sash/dirent.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef DIRENT_H_
-#define DIRENT_H_
-
-typedef struct {} DIR;
-
-struct dirent {
- char *d_name;
-};
-
-#endif // DIRENT_H_
diff --git a/src/sash/sash.1 b/src/sash/sash.1
deleted file mode 100644
index 8c61b9b..0000000
--- a/src/sash/sash.1
+++ /dev/null
@@ -1,591 +0,0 @@
-.TH SASH 1
-.SH NAME
-sash \- stand-alone shell with built-in commands
-.SH SYNOPSYS
-.B sash [-c command] [-f fileName ] [-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 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 non-wildcard meta-characters in the command.
-.PP
-More importantly, however,
-is that many of the standard system commands are built-in to
-.BR sash .
-These built-in commands are:
-.PP
-.nf
- -ar, -chattr, -chgrp, -chmod, -chown, -chroot, -cmp,
- -cp, -dd, -echo, -ed, -grep, -file, -find, -gunzip,
- -gzip, -kill, -losetup, -ln, -ls, -lsattr, -mkdir,
- -mknod, -more, -mount, -mv, -pivot_root, -printenv, -pwd,
- -rm, -rmdir, -sum, -sync, -tar, -touch, -umount, -where
-.fi
-.PP
-These commands are generally similar to the standard programs with similar
-names. However, they are simpler and cruder than the external programs,
-and so many of the options are not implemented. The restrictions for each
-built-in command are described later.
-.PP
-The built-in commands which correspond to external programs begin with a
-dash character in order to distinguish them from the external programs.
-So typing "ls", for example, will attempt to run the real
-.B ls
-program.
-If "-ls" is typed, then the built-in command which mimics
-.B ls
-is called.
-.PP
-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.
-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
-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 .
-If an argument is given, it will list only those built-in commands
-which contain the given argument as a sub-string.
-Each built-in command is described below in more detail.
-.PP
-.TP
-.B alias [name [command]]
-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.
-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.
-.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
-is provided, then the current directory is changed to the
-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 -chattr [+i] [-i] [+a] [-a] fileName ...
-Change the attributes of the specified files on an ext2 or ext3 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.
-This command is only available on Linux.
-.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 ...
-Change the mode of the specified list of files. The
-.I mode
-argument
-can only be an octal value.
-.TP
-.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 -chroot path
-Changes the root directory to that specified in
-.I path.
-This directory
-will be used for path names beginning with /. The root directory is
-inherited by all children of the current process.
-.TP
-.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
-Copies one or more files from the
-.I srcName
-to the
-.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.
-.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.
-The
-.I if
-and
-.I of
-arguments must be provided, so stdin and stdout cannot
-be specified. The
-.I bs
-argument is the block size, and is a numeric
-value (which defaults to 512 bytes).
-.I Count
-is the number of blocks
-to be copied (which defaults to end of file for the input file).
-.I Skip
-is the number of blocks to ignore before copying (seek is used
-if possible, and the default is 0).
-.I Seek
-is the number of blocks to
-seek in the output file before writing (and defaults to 0). Any of
-the numeric decimal values can have one or more trailing letters
-from the set 'kbw', which multiplies the value by 1024, 512, and 2
-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 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]
-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.
-Line numbers can be constants, ".", "$", "'x",
-.RI / string /
-and simple
-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]
-Execute the specified program with the specified arguments.
-This replaces
-.B sash
-completely by the executed program.
-.TP
-.B exit
-Quit from
-.BR sash .
-.TP
-.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 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,
-(ie, not a regular expression). If -i is given, then case is
-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]
-Uncompress one or more files that had been compressed using the
-.I gzip
-or
-.I compress
-algorithms.
-If the -o option is not given,
-then each of the input file names must have one of the
-extensions ".gz", ".tgz", or ".Z",
-and those files will be replaced by the uncompressed versions of those files.
-The original files will be deleted after the output files have been
-successfully created.
-The uncompressed versions of the files have the same names as the original
-file names, except for a simple modification of their extensions.
-If an extension is ".tgz", then the extension is replaced by ".tar".
-Otherwise, the ".gz" or ".Z" extension is removed.
-.sp
-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 .
-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 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.
-.sp
-This command is only available if
-.B sash
-was compiled to use the gzip library.
-.TP
-.B -gzip inputFileName ... [-o outputPath]
-Compresses one or more files using the
-.I gzip
-algorithm.
-If the -o option is not given,
-then each of the input file names will be replaced by the compressed
-versions of those files,
-The original files will be deleted after the output files have been
-successfully created.
-The compressed versions of the files have the same names as the original
-file names, except for a simple modification of the extensions.
-If an extension is ".tar", then the extension is replaced by ".tgz".
-Otherwise, the ".gz" extension is added.
-.sp
-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 .
-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.
-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.
-.sp
-This command is only available if
-.B sash
-was compiled to use the gzip library.
-.TP
-.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 numeric value, or one of the special values HUP, INT,
-QUIT, KILL, TERM, STOP, CONT, USR1 or USR2.
-If no signal is specified then SIGTERM is used.
-.TP
-.B -losetup [-d] loopDev [file]
-Associates loopback devices with files on the system. If
-.I -d
-is not given,
-the loopback device
-.I loopDev
-is associated with
-.I file.
-If
-.I -d
-is given,
-.I loopDev
-is unassociated with the file it's currently configured for.
-.TP
-.B -ln [-s] srcName ... destName
-Links one or more files from the
-.I srcName
-to the specified
-.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.
-The default links are hard links. Using -s makes symbolic links.
-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, -n, -i, -d, and -F.
-The -l option produces a long listing giving the normal 'ls' information.
-The -n option is like -l except that numeric user and group ids are shown.
-The -i option displays the inode numbers of the files.
-The -d option displays information about a directory, instead of the
-files within it.
-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 an ext2 or ext3 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.
-This command is only available on Linux.
-.TP
-.B -mkdir dirName ...
-Creates the specified directories. They are created with the
-default permissions.
-.TP
-.B -mknod fileName type major minor
-Creates a special device node, either a character file or a block
-file.
-.I Filename
-is the name of the node.
-.I Type
-is either 'c' or 'd'.
-.I Major
-is the major device number.
-.I Minor
-is the minor device number.
-Both of these numbers are decimal.
-.TP
-.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] [-s] [-e] [-m] devName dirName
-Mount a filesystem on a directory name.
-The -t option specifies the type of filesystem being mounted,
-and defaults to "ext3" for Linux and "ffs" for BSD.
-The -r option indicates to mount the filesystem read-only.
-The -s option indicates to mount the filesystem no-suid.
-The -e option indicates to mount the filesystem no-exec.
-The -m option indicates to remount an already mounted filesystem.
-The -m option is only available on Linux.
-.TP
-.B -mv srcName ... destName
-Moves one or more files from the
-.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
-this fails because of the files being on different filesystems,
-then copies and deletes are done instead.
-.TP
-.B -pivot_root newRoot putOld
-Moves the root file system of the current process to the directory
-.I putOld
-and makes
-.I newRoot
-the new root file system of the current process.
-.TP
-.B -printenv [name]
-If
-.I name
-is not given, this prints out the values of all the current
-environment variables. If
-.I name
-is given, then only that environment variable value is printed.
-.TP
-.B prompt [word] ...
-Sets the prompt string that is displayed before reading of a
-command. A space is always added to the specified prompt.
-.TP
-.B -pwd
-Prints the current working directory.
-.TP
-.B quit
-Exits from
-.BR sash .
-.TP
-.B -rm fileName ...
-Removes one or more files.
-.TP
-.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 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 [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
-.B umask [mask]
-If
-.I mask
-is given, sets the "umask" value used for initializing the
-permissions of newly created files. If
-.I mask
-is not given, then the
-current umask value is printed. The mask is an octal value.
-.TP
-.B -umount [-f] 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.
-The -f option unmounts the filesystem even if it is being used.
-The -f option is only available on BSD.
-.TP
-.B unalias name
-Remove the definition for the specified alias.
-.TP
-.B -where program
-Prints out all of paths defined by the PATH environment variable where the
-specified program exists. If the program exists but cannot be executed,
-then the reason is also printed.
-.SH OPTIONS
-There are several command line options to
-.BR sash .
-.PP
-The -c option executes the next argument as a command (including embedded
-spaces to separate the arguments of the command), and then exits.
-.PP
-The -f option executes the commands contained in the file name specified
-by the next argument, and then exits.
-This feature can be used to create executable scripts for
-.B sash
-by starting the script file with a line similar to:
-.nf
- #! /bin/sash -f
-.fi
-.PP
-The -p option takes the next argument as the prompt string to be used
-when prompting for commands.
-.PP
-The -q option makes
-.B sash
-quiet, which simply means that it doesn't print its introduction line
-when it starts.
-This option is also implied if the -c or -f options are used.
-.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.
-Similar concepts should exist for other boot loaders and operating systems.
-.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 its purpose is lost.
-Note that even if the rest of the program is linked statically, the
-password and group lookup routines in the C library can still be dynamic.
-For that reason, if there are problems then it might be necessary to
-only use numeric ids for the -chown and -chgrp commands and to use
-the -n option instead of -l for the -ls command.
-.PP
-Several other system commands might be necessary for system recovery,
-but aren't built-in to
-.BR sash .
-.SH AUTHOR
-.nf
-David I. Bell
-dbell@tip.net.au
-5 March 2014
-.fi
diff --git a/src/sash/sash.c b/src/sash/sash.c
deleted file mode 100644
index 8c15711..0000000
--- a/src/sash/sash.c
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Stand-alone shell for system maintainance for Linux.
- * This program should NOT be built using shared libraries.
- */
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <errno.h>
-
-#include "sash.h"
-
-
-static const char * const version = "3.8";
-
-
-/*
- * 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
-
-
-/*
- * One entry of the command table.
- */
-typedef struct
-{
- const char * name;
- int (*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", do_cd, 1, 2,
- "Change current directory",
- "[dirName]"
- },
-
-#if HAVE_LINUX_ATTR
- {
- "-chattr", do_chattr, 3, INFINITE_ARGS,
- "Change ext2 file attributes",
- "[+i] [-i] [+a] [-a] fileName ..."
- },
-#endif
-
- {
- "-chgrp", do_chgrp, 3, INFINITE_ARGS,
- "Change the group id of some files",
- "gid fileName ..."
- },
-
- {
- "-chmod", do_chmod, 3, INFINITE_ARGS,
- "Change the protection of some files",
- "mode fileName ..."
- },
-
- {
- "-chown", do_chown, 3, INFINITE_ARGS,
- "Change the owner id of some files",
- "uid fileName ..."
- },
-
- {
- "-cmp", do_cmp, 3, 3,
- "Compare two files for equality",
- "fileName1 fileName2"
- },
-
- {
- "-cp", do_cp, 3, INFINITE_ARGS,
- "Copy files",
- "srcName ... destName"
- },
-
-#ifdef HAVE_LINUX_CHROOT
- {
- "-chroot", do_chroot, 2, 2,
- "change root file system",
- "new_root_dir"
- },
-#endif
-
- {
- "-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]"
- },
-
- {
- "exec", do_exec, 2, INFINITE_ARGS,
- "Execute another program in place of this sash process",
- "fileName [args]"
- },
-
- {
- "exit", do_exit, 1, 2,
- "Exit from sash",
- "[exit value]"
- },
-
- {
- "-file", do_file, 1, INFINITE_ARGS,
- "Describe information about files",
- "fileName ..."
- },
-
- {
- "-find", do_find, 2, INFINITE_ARGS,
- "Find files in a directory tree meeting some conditions",
- "dirName [-xdev] [-type chars] [-name pattern] [-size minSize]"
- },
-
- {
- "-grep", do_grep, 3, INFINITE_ARGS,
- "Look for lines containing a word in some files",
- "[-in] word fileName ..."
- },
-
-#if HAVE_GZIP
- {
- "-gunzip", do_gunzip, 2, INFINITE_ARGS,
- "Uncompress files which were saved in GZIP or compress format",
- "fileName ... [-o outputPath]"
- },
-
- {
- "-gzip", do_gzip, 2, INFINITE_ARGS,
- "Compress files into GZIP format",
- "fileName ... [-o outputPath]"
- },
-#endif
-
- {
- "help", do_help, 1, 2,
- "Print help about a command",
- "[word]"
- },
-
- {
- "-kill", do_kill, 2, INFINITE_ARGS,
- "Send a signal to the specified process",
- "[-sig] pid ..."
- },
-
-#ifdef HAVE_LINUX_LOSETUP
- {
- "-losetup", do_losetup, 3, 3,
- "Associate a loopback device with a file",
- "[-d] device\n -losetup device filename"
- },
-#endif
-
- {
- "-ln", do_ln, 3, INFINITE_ARGS,
- "Link one fileName to another",
- "[-s] srcName ... destName"
- },
-
- {
- "-ls", do_ls, 1, INFINITE_ARGS,
- "List information about files or directories",
- "[-lidFC] fileName ..."
- },
-
-#if HAVE_LINUX_ATTR
- {
- "-lsattr", do_lsattr, 2, INFINITE_ARGS,
- "List ext2 file attributes",
- "fileName ..."
- },
-#endif
-
- {
- "-mkdir", do_mkdir, 2, INFINITE_ARGS,
- "Create a directory",
- "dirName ..."
- },
-
- {
- "-mknod", do_mknod, 5, 5,
- "Create a special type of file",
- "fileName type major minor"
- },
-
- {
- "-more", do_more, 2, INFINITE_ARGS,
- "Type file contents page by page",
- "fileName ..."
- },
-
- {
- "-mount", do_mount, 3, INFINITE_ARGS,
- "Mount or remount a filesystem on a directory",
-#if HAVE_LINUX_MOUNT
- "[-t type] [-r] [-s] [-e] [-m] devName dirName"
-#elif HAVE_BSD_MOUNT
- "[-t type] [-r] [-s] [-e] devName dirName"
-#else
- "[-t type] devName dirName"
-#endif
- },
-
- {
- "-mv", do_mv, 3, INFINITE_ARGS,
- "Move or rename files",
- "srcName ... destName"
- },
-
-#ifdef HAVE_LINUX_PIVOT
- {
- "-pivot_root", do_pivot_root, 3, 3,
- "pivot the root file system",
- "new_dir old_dir"
- },
-#endif
-
- {
- "-printenv", do_printenv, 1, 2,
- "Print environment variables",
- "[name]"
- },
-
- {
- "prompt", do_prompt, 2, INFINITE_ARGS,
- "Set the prompt string for sash",
- "string"
- },
-
- {
- "-pwd", do_pwd, 1, 1,
- "Print the current working directory",
- ""
- },
-
- {
- "quit", do_exit, 1, 1,
- "Exit from sash",
- ""
- },
-
- {
- "-rm", do_rm, 2, INFINITE_ARGS,
- "Remove the specified files",
- "fileName ..."
- },
-
- {
- "-rmdir", do_rmdir, 2, INFINITE_ARGS,
- "Remove the specified empty directories",
- "dirName ..."
- },
-
- {
- "setenv", do_setenv, 3, 3,
- "Set an environment variable value",
- "name value"
- },
-
- {
- "source", do_source, 2, 2,
- "Read commands from the specified file",
- "fileName"
- },
-
- {
- "-sum", do_sum, 2, INFINITE_ARGS,
- "Calculate checksums of the specified files",
- "fileName ..."
- },
-
- {
- "-sync", do_sync, 1, 1,
- "Sync the disks to force cached data to them",
- ""
- },
-
- {
- "-tar", do_tar, 2, INFINITE_ARGS,
- "Create, extract, or list files from a TAR file",
- "[cxtv]f tarFileName fileName ..."
- },
-
- {
- "-touch", do_touch, 2, INFINITE_ARGS,
- "Update times or create the specified files",
- "fileName ..."
- },
-
- {
- "umask", do_umask, 1, 2,
- "Set the umask value for file protections",
- "[mask]"
- },
-
- {
-#if HAVE_BSD_MOUNT
- "-umount", do_umount, 2, 3,
- "Unmount a filesystem",
- "[-f] fileName"
-#else
- "-umount", do_umount, 2, 2,
- "Unmount a filesystem",
- "fileName"
-#endif
- },
-
- {
- "unalias", do_unalias, 2, 2,
- "Remove a command alias",
- "name"
- },
-
- {
- "-where", do_where, 2, 2,
- "Type the location of a program",
- "program"
- },
-
- {
- NULL, 0, 0, 0,
- NULL,
- NULL
- }
-};
-
-
-/*
- * The definition of an command alias.
- */
-typedef struct
-{
- char * name;
- char * value;
-} Alias;
-
-
-/*
- * Local data.
- */
-static Alias * aliasTable;
-static int aliasCount;
-
-static FILE * sourcefiles[MAX_SOURCE];
-static int sourceCount;
-
-static BOOL intCrlf = TRUE;
-static char * prompt;
-
-
-/*
- * Local procedures.
- */
-static void catchInt(int);
-static void catchQuit(int);
-static int readFile(const char * name);
-static int command(const char * cmd);
-static BOOL tryBuiltIn(const char * cmd);
-static int runCmd(const char * cmd);
-static void childProcess(const char * cmd);
-static void showPrompt(void);
-static void usage(void);
-static Alias * findAlias(const char * name);
-static void expandVariable(char * name);
-
-
-/*
- * Global interrupt flag.
- */
-BOOL intFlag;
-
-
-int
-main(int argc, const char ** argv)
-{
- const char * cp;
- const char * singleCommand;
- const char * commandFile;
- BOOL quietFlag;
- BOOL aliasFlag;
- BOOL interactiveFlag;
- char buf[PATH_LEN];
-
- singleCommand = NULL;
- commandFile = NULL;
- quietFlag = FALSE;
- aliasFlag = FALSE;
- interactiveFlag = FALSE;
-
- /*
- * Look for options.
- */
- argv++;
- argc--;
-
- while ((argc > 0) && (**argv == '-'))
- {
- cp = *argv++ + 1;
- argc--;
-
- while (*cp) switch (*cp++)
- {
- case '-':
- /*
- * Ignore. This is so that we can be
- * run from login.
- */
- break;
-
- case 'c':
- /*
- * Execute specified command.
- */
- if ((argc != 1) || singleCommand || interactiveFlag)
- usage();
-
- singleCommand = *argv++;
- argc--;
-
- break;
-
- case 'f':
- /*
- * Execute commands from file.
- * This is used for sash script files.
- * The quiet flag is also set.
- */
- if ((argc != 1) || commandFile)
- usage();
-
- quietFlag = TRUE;
- commandFile = *argv++;
- argc--;
-
- break;
-
- case 'i':
- /*
- * Be an interactive shell
- * ..is a no-op, but some contexts require this
- * ..interactiveFlag is to avoid -ic as a legacy
- */
- if (singleCommand)
- usage();
-
- interactiveFlag = TRUE;
- break;
-
- case 'p':
- /*
- * Set the prompt string.
- */
- if ((argc <= 0) || (**argv == '-'))
- usage();
-
- if (prompt)
- free(prompt);
-
- prompt = strdup(*argv++);
- argc--;
-
- break;
-
- case 'q':
- quietFlag = TRUE;
- break;
-
- case 'a':
- aliasFlag = TRUE;
- break;
-
- case 'h':
- case '?':
- usage();
- break;
-
- default:
- fprintf(stderr, "Unknown option -%c\n", cp[-1]);
-
- return 1;
- }
- }
-
- /*
- * No more arguments are allowed.
- */
- if (argc > 0)
- usage();
-
- /*
- * Default our path if it is not set.
- */
- 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.
- */
- if (singleCommand)
- {
- return command(singleCommand);
- }
-
- /*
- * Print a hello message unless we are told to be silent.
- */
- if (!quietFlag && isatty(STDIN))
- {
- printf("Stand-alone shell (version %s)\n", version);
-
- if (aliasFlag)
- printf("Built-in commands are aliased to standard commands\n");
- }
-
- signal(SIGINT, catchInt);
- signal(SIGQUIT, catchQuit);
-
- /*
- * Execute the user's alias file if present.
- */
- cp = getenv("HOME");
-
- if (cp)
- {
- strcpy(buf, cp);
- strcat(buf, "/");
- strcat(buf, ".aliasrc");
-
- if ((access(buf, 0) == 0) || (errno != ENOENT))
- readFile(buf);
- }
-
- /*
- * Read commands from stdin or from a command file.
- */
- return readFile(commandFile);
-
-}
-
-
-/*
- * Read commands from the specified file.
- * A null name pointer indicates to read from stdin.
- */
-static int
-readFile(const char * name)
-{
- FILE * fp;
- int cc;
- BOOL ttyFlag;
- char buf[CMD_LEN];
- int r = 0;
-
- if (sourceCount >= MAX_SOURCE)
- {
- fprintf(stderr, "Too many source files\n");
-
- return 1;
- }
-
- fp = stdin;
-
- if (name)
- {
- fp = fopen(name, "r");
-
- if (fp == NULL)
- {
- perror(name);
-
- return 1;
- }
- }
-
- sourcefiles[sourceCount++] = fp;
-
- ttyFlag = isatty(fileno(fp));
-
- while (TRUE)
- {
- if (ttyFlag)
- showPrompt();
-
- if (intFlag && !ttyFlag && (fp != stdin))
- {
- fclose(fp);
- sourceCount--;
-
- return 1;
- }
-
- if (fgets(buf, CMD_LEN - 1, fp) == NULL)
- {
- if (ferror(fp) && (errno == EINTR))
- {
- clearerr(fp);
-
- continue;
- }
-
- break;
- }
-
- cc = strlen(buf);
-
- if (buf[cc - 1] == '\n')
- cc--;
-
- while ((cc > 0) && isBlank(buf[cc - 1]))
- cc--;
-
- buf[cc] = '\0';
-
- r = command(buf);
- }
-
- if (ferror(fp))
- {
- perror("Reading command line");
-
- if (fp == stdin)
- exit(1);
- }
-
- clearerr(fp);
-
- if (fp != stdin)
- fclose(fp);
-
- sourceCount--;
-
- return r;
-}
-
-
-/*
- * Parse and execute one null-terminated command line string.
- * This breaks the command line up into words, checks to see if the
- * command is an alias, and expands wildcards.
- */
-static int
-command(const char * cmd)
-{
- const char * endCmd;
- const Alias * alias;
- char newCommand[CMD_LEN];
- char cmdName[CMD_LEN];
-
- /*
- * Rest the interrupt flag and free any memory chunks that
- * were allocated by the previous command.
- */
- intFlag = FALSE;
-
- freeChunks();
-
- /*
- * Skip leading blanks.
- */
- while (isBlank(*cmd))
- cmd++;
-
- /*
- * If the command is empty or is a comment then ignore it.
- */
- if ((*cmd == '\0') || (*cmd == '#'))
- return 0;
-
- /*
- * Look for the end of the command name and then copy the
- * command name to a buffer so we can null terminate it.
- */
- endCmd = cmd;
-
- while (*endCmd && !isBlank(*endCmd))
- endCmd++;
-
- memcpy(cmdName, cmd, endCmd - cmd);
-
- cmdName[endCmd - cmd] = '\0';
-
- /*
- * 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;
- }
-
- /*
- * Expand simple environment variables
- */
- while (strstr(cmd, "$(")) expandVariable((char *)cmd);
-
- /*
- * Now look for the command in the builtin table, and execute
- * the command if found.
- */
- if (tryBuiltIn(cmd))
- return 0; /* This is a blatant lie */
-
- /*
- * The command is not a built-in, so run the program along
- * the PATH list.
- */
- return runCmd(cmd);
-}
-
-
-/*
- * Try to execute a built-in command.
- * Returns TRUE if the command is a built in, whether or not the
- * command succeeds. Returns FALSE if this is not a built-in command.
- */
-static BOOL
-tryBuiltIn(const char * cmd)
-{
- const char * endCmd;
- const CommandEntry * entry;
- int argc;
- const char ** argv;
- char cmdName[CMD_LEN];
-
- /*
- * Look for the end of the command name and then copy the
- * command name to a buffer so we can null terminate it.
- */
- endCmd = cmd;
-
- while (*endCmd && !isBlank(*endCmd))
- endCmd++;
-
- memcpy(cmdName, cmd, endCmd - cmd);
-
- cmdName[endCmd - cmd] = '\0';
-
- /*
- * Search the command table looking for the command name.
- */
- for (entry = commandEntryTable; entry->name != NULL; entry++)
- {
- if (strcmp(entry->name, cmdName) == 0)
- break;
- }
-
- /*
- * If the command is not a built-in, return indicating that.
- */
- 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;
-
- /*
- * Give a usage string if the number of arguments is too large
- * or too small.
- */
- if ((argc < entry->minArgs) || (argc > entry->maxArgs))
- {
- fprintf(stderr, "usage: %s %s\n", entry->name, entry->usage);
-
- return TRUE;
- }
-
- /*
- * Call the built-in function with the argument list.
- */
- entry->func(argc, argv);
-
- return TRUE;
-}
-
-
-/*
- * Execute the specified command either by forking and executing
- * the program ourself, or else by using the shell. Returns the
- * exit status, or -1 if the program cannot be executed at all.
- */
-static int
-runCmd(const char * cmd)
-{
- const char * cp;
- BOOL magic;
- pid_t pid;
- int status;
-
- /*
- * Check the command for any magic shell characters
- * except for quoting.
- */
- magic = FALSE;
-
- for (cp = cmd; *cp; cp++)
- {
- if ((*cp >= 'a') && (*cp <= 'z'))
- continue;
-
- if ((*cp >= 'A') && (*cp <= 'Z'))
- continue;
-
- if (isDecimal(*cp))
- continue;
-
- if (isBlank(*cp))
- continue;
-
- if ((*cp == '.') || (*cp == '/') || (*cp == '-') ||
- (*cp == '+') || (*cp == '=') || (*cp == '_') ||
- (*cp == ':') || (*cp == ',') || (*cp == '\'') ||
- (*cp == '"'))
- {
- continue;
- }
-
- magic = TRUE;
- }
-
- /*
- * If there were any magic characters used then run the
- * command using the shell.
- */
- if (magic)
- return trySystem(cmd);
-
- /*
- * No magic characters were in the command, so we can do the fork
- * and exec ourself.
- */
- pid = fork();
-
- if (pid < 0)
- {
- perror("fork failed");
-
- return -1;
- }
-
- /*
- * If we are the child process, then go execute the program.
- */
- if (pid == 0)
- childProcess(cmd);
-
- /*
- * We are the parent process.
- * Wait for the child to complete.
- */
- status = 0;
- intCrlf = FALSE;
-
- while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR))
- ;
-
- intCrlf = TRUE;
-
- if (pid < 0)
- {
- fprintf(stderr, "Error from waitpid: %s", strerror(errno));
-
- return -1;
- }
-
- if (WIFSIGNALED(status))
- {
- fprintf(stderr, "pid %ld: killed by signal %d\n",
- (long) pid, WTERMSIG(status));
-
- return -1;
- }
-
- return WEXITSTATUS(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;
-
- /*
- * Close any extra file descriptors we have opened.
- */
- while (--sourceCount >= 0)
- {
- if (sourcefiles[sourceCount] != stdin)
- fclose(sourcefiles[sourceCount]);
- }
-
- /*
- * Break the command line up into individual arguments.
- * If this fails, then run the shell to execute the command.
- */
- if (!makeArgs(cmd, &argc, &argv))
- {
- int status = trySystem(cmd);
-
- if (status == -1)
- exit(99);
-
- exit(status);
- }
-
- /*
- * Try to execute the program directly.
- */
- execvp(argv[0], (char **) argv);
-
- /*
- * The exec failed, so try to run the command using the shell
- * in case it is a shell script.
- */
- if (errno == ENOEXEC)
- {
- int status = trySystem(cmd);
-
- if (status == -1)
- exit(99);
-
- exit(status);
- }
-
- /*
- * There was something else wrong, complain and exit.
- */
- perror(argv[0]);
- exit(1);
-}
-
-
-int
-do_help(int argc, const char ** argv)
-{
- const CommandEntry * entry;
- const char * str;
-
- str = NULL;
-
- if (argc == 2)
- str = argv[1];
-
- /*
- * 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 0;
- }
- }
- }
-
- /*
- * 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);
- }
- }
-
- return 0;
-}
-
-
-int
-do_alias(int argc, const char ** argv)
-{
- const char * name;
- char * value;
- Alias * alias;
- int count;
- char buf[CMD_LEN];
-
- if (argc < 2)
- {
- count = aliasCount;
-
- for (alias = aliasTable; count-- > 0; alias++)
- printf("%s\t%s\n", alias->name, alias->value);
-
- return 0;
- }
-
- name = argv[1];
-
- if (argc == 2)
- {
- alias = findAlias(name);
-
- if (alias)
- printf("%s\n", alias->value);
- else
- {
- fprintf(stderr, "Alias \"%s\" is not defined\n", name);
-
- return 1;
- }
-
- return 0;
- }
-
- if (strcmp(name, "alias") == 0)
- {
- fprintf(stderr, "Cannot alias \"alias\"\n");
-
- return 1;
- }
-
- if (!makeString(argc - 2, argv + 2, buf, CMD_LEN))
- return 1;
-
- value = malloc(strlen(buf) + 1);
-
- if (value == NULL)
- {
- fprintf(stderr, "No memory for alias value\n");
-
- return 1;
- }
-
- strcpy(value, buf);
-
- alias = findAlias(name);
-
- if (alias)
- {
- free(alias->value);
- alias->value = value;
-
- return 0;
- }
-
- if ((aliasCount % ALIAS_ALLOC) == 0)
- {
- count = aliasCount + ALIAS_ALLOC;
-
- if (aliasTable)
- {
- alias = (Alias *) realloc(aliasTable,
- sizeof(Alias) * count);
- }
- else
- alias = (Alias *) malloc(sizeof(Alias) * count);
-
- if (alias == NULL)
- {
- free(value);
- fprintf(stderr, "No memory for alias table\n");
-
- return 1;
- }
-
- aliasTable = alias;
- }
-
- alias = &aliasTable[aliasCount];
-
- alias->name = malloc(strlen(name) + 1);
-
- if (alias->name == NULL)
- {
- free(value);
- fprintf(stderr, "No memory for alias name\n");
-
- return 1;
- }
-
- strcpy(alias->name, name);
- alias->value = value;
- aliasCount++;
-
- return 0;
-}
-
-
-/*
- * Build aliases for all of the built-in commands which start with a dash,
- * using the names without the dash.
- */
-int
-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);
- }
-
- return 0;
-}
-
-
-/*
- * Look up an alias name, and return a pointer to it.
- * Returns NULL if the name does not exist.
- */
-static Alias *
-findAlias(const char * name)
-{
- Alias * alias;
- int count;
-
- count = aliasCount;
-
- for (alias = aliasTable; count-- > 0; alias++)
- {
- if (strcmp(name, alias->name) == 0)
- return alias;
- }
-
- return NULL;
-}
-
-
-int
-do_source(int argc, const char ** argv)
-{
- return readFile(argv[1]);
-}
-
-
-int
-do_exec(int argc, const char ** argv)
-{
- const char * name;
-
- name = argv[1];
-
- while (--sourceCount >= 0)
- {
- if (sourcefiles[sourceCount] != stdin)
- fclose(sourcefiles[sourceCount]);
- }
-
- argv[argc] = NULL;
-
- execvp(name, (char **) argv + 1);
- perror(name);
-
- return 1;
-}
-
-
-int
-do_prompt(int argc, const char ** argv)
-{
- char * cp;
- char buf[CMD_LEN];
-
- if (!makeString(argc - 1, argv + 1, buf, CMD_LEN))
- return 1;
-
- cp = malloc(strlen(buf) + 2);
-
- if (cp == NULL)
- {
- fprintf(stderr, "No memory for prompt\n");
-
- return 1;
- }
-
- strcpy(cp, buf);
- strcat(cp, " ");
-
- if (prompt)
- free(prompt);
-
- prompt = cp;
-
- return 0;
-}
-
-
-int
-do_unalias(int argc, const char ** argv)
-{
- Alias * alias;
-
- while (--argc > 0)
- {
- alias = findAlias(*++argv);
-
- if (alias == NULL)
- continue;
-
- free(alias->name);
- free(alias->value);
- aliasCount--;
- alias->name = aliasTable[aliasCount].name;
- alias->value = aliasTable[aliasCount].value;
- }
-
- return 0;
-}
-
-
-/*
- * Display the prompt string.
- */
-static void
-showPrompt(void)
-{
- const char * cp;
-
- cp = "> ";
-
- if (prompt)
- cp = prompt;
-
- tryWrite(STDOUT, cp, strlen(cp));
-}
-
-
-static void
-catchInt(int val)
-{
- signal(SIGINT, catchInt);
-
- intFlag = TRUE;
-
- if (intCrlf)
- tryWrite(STDOUT, "\n", 1);
-}
-
-
-static void
-catchQuit(int val)
-{
- signal(SIGQUIT, catchQuit);
-
- intFlag = TRUE;
-
- if (intCrlf)
- tryWrite(STDOUT, "\n", 1);
-}
-
-
-/*
- * Print the usage information and quit.
- */
-static void
-usage(void)
-{
- fprintf(stderr, "Stand-alone shell (version %s)\n", version);
- fprintf(stderr, "\n");
- fprintf(stderr, "Usage: sash [-a] [-q] [-f fileName] [-c command] [-p prompt] [-i]\n");
-
- exit(1);
-}
-
-
-/*
- * Expand one environment variable: Syntax $(VAR)
- */
-static void
-expandVariable(char * cmd)
-{
- char tmp[CMD_LEN];
- char *cp;
- char *ep;
-
- strcpy(tmp, cmd);
- cp = strstr(tmp, "$(");
- if (cp) {
- *cp++ = '\0';
- strcpy(cmd, tmp);
- ep = ++cp;
- while (*ep && (*ep != ')')) ep++;
- if (*ep == ')') *ep++ = '\0';
- cp = getenv(cp);
- if (cp) strcat(cmd, cp);
- strcat(cmd, ep);
- }
- return;
-}
-
-/* END CODE */
diff --git a/src/sash/sash.h b/src/sash/sash.h
deleted file mode 100644
index b0ed254..0000000
--- a/src/sash/sash.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Definitions for stand-alone shell for system maintainance for Linux.
- */
-
-#ifndef SASH_H
-#define SASH_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <memory.h>
-#include <time.h>
-#include <ctype.h>
-
-#if __OpenBSD__
-#include <sys/param.h>
-#endif
-
-#if __Linux__
-#include <malloc.h>
-#endif
-
-
-#define PATH_LEN 1024
-#define CMD_LEN 10240
-#define ALIAS_ALLOC 20
-#define EXPAND_ALLOC 1024
-#define STDIN 0
-#define STDOUT 1
-#define MAX_SOURCE 10
-#define BUF_SIZE 8192
-
-
-#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;
-
-#define FALSE ((BOOL) 0)
-#define TRUE ((BOOL) 1)
-
-
-/*
- * Built-in command functions.
- */
-extern int do_alias(int argc, const char ** argv);
-extern int do_aliasall(int argc, const char ** argv);
-extern int do_cd(int argc, const char ** argv);
-extern int do_exec(int argc, const char ** argv);
-extern int do_exit(int argc, const char ** argv);
-extern int do_prompt(int argc, const char ** argv);
-extern int do_source(int argc, const char ** argv);
-extern int do_umask(int argc, const char ** argv);
-extern int do_unalias(int argc, const char ** argv);
-extern int do_help(int argc, const char ** argv);
-extern int do_ln(int argc, const char ** argv);
-extern int do_cp(int argc, const char ** argv);
-extern int do_mv(int argc, const char ** argv);
-extern int do_rm(int argc, const char ** argv);
-extern int do_chmod(int argc, const char ** argv);
-extern int do_mkdir(int argc, const char ** argv);
-extern int do_rmdir(int argc, const char ** argv);
-extern int do_mknod(int argc, const char ** argv);
-extern int do_chown(int argc, const char ** argv);
-extern int do_chgrp(int argc, const char ** argv);
-extern int do_sum(int argc, const char ** argv);
-extern int do_sync(int argc, const char ** argv);
-extern int do_printenv(int argc, const char ** argv);
-extern int do_more(int argc, const char ** argv);
-extern int do_cmp(int argc, const char ** argv);
-extern int do_touch(int argc, const char ** argv);
-extern int do_ls(int argc, const char ** argv);
-extern int do_dd(int argc, const char ** argv);
-extern int do_tar(int argc, const char ** argv);
-extern int do_ar(int argc, const char ** argv);
-extern int do_mount(int argc, const char ** argv);
-extern int do_umount(int argc, const char ** argv);
-extern int do_setenv(int argc, const char ** argv);
-extern int do_pwd(int argc, const char ** argv);
-extern int do_echo(int argc, const char ** argv);
-extern int do_kill(int argc, const char ** argv);
-extern int do_grep(int argc, const char ** argv);
-extern int do_file(int argc, const char ** argv);
-extern int do_find(int argc, const char ** argv);
-extern int do_ed(int argc, const char ** argv);
-extern int do_where(int argc, const char ** argv);
-
-#if HAVE_GZIP
-extern int do_gzip(int argc, const char ** argv);
-extern int do_gunzip(int argc, const char ** argv);
-#endif
-
-#if HAVE_LINUX_ATTR
-extern int do_lsattr(int argc, const char ** argv);
-extern int do_chattr(int argc, const char ** argv);
-#endif
-
-#if HAVE_LINUX_CHROOT
-extern int do_chroot(int argc, const char ** argv);
-#endif
-
-#if HAVE_LINUX_LOSETUP
-extern int do_losetup(int argc, const char ** argv);
-#endif
-
-#if HAVE_LINUX_PIVOT
-extern int do_pivot_root(int argc, const char ** argv);
-extern int pivot_root(const char *new_root, const char *put_old);
-#endif
-
-
-/*
- * Global utility routines.
- */
-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 trySystem(const char * cmd);
-extern void tryWrite(int fd, const char * buf, int len);
-extern int fullWrite(int fd, const char * buf, int len);
-extern int fullRead(int fd, char * buf, int len);
-extern void checkStatus(const char * name, int status);
-extern BOOL match(const char * text, const char * pattern);
-
-extern const char * buildName
- (const char * dirName, const char * fileName);
-
-extern BOOL makeArgs
- (const char * cmd, int * argcPtr, const char *** argvPtr);
-
-extern BOOL copyFile
- (const char * srcName, const char * destName, BOOL setModes);
-
-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;
-
-#endif
-
-/* END CODE */
diff --git a/src/sash/utils.c b/src/sash/utils.c
deleted file mode 100644
index f6bee83..0000000
--- a/src/sash/utils.c
+++ /dev/null
@@ -1,1144 +0,0 @@
-/*
- * Copyright (c) 2014 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Utility routines.
- */
-
-#include "sash.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "dirent.h"
-#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 CHUNK_INIT_SIZE 4
-
-struct chunk
-{
- CHUNK * next;
- char data[CHUNK_INIT_SIZE]; /* actually of varying length */
-};
-
-
-static CHUNK * chunkList;
-
-
-
-/*
- * Return the standard ls-like mode string from a file mode.
- * This is static and so is overwritten on each call.
- */
-const char *
-modeString(int mode)
-{
- static char buf[12];
-
- strcpy(buf, "----------");
-
- /*
- * Fill in the file type.
- */
- if (S_ISDIR(mode))
- buf[0] = 'd';
- if (S_ISCHR(mode))
- buf[0] = 'c';
- if (S_ISBLK(mode))
- buf[0] = 'b';
- if (S_ISFIFO(mode))
- buf[0] = 'p';
-#ifdef S_ISLNK
- if (S_ISLNK(mode))
- buf[0] = 'l';
-#endif
-#ifdef S_ISSOCK
- if (S_ISSOCK(mode))
- buf[0] = 's';
-#endif
-
- /*
- * Now fill in the normal file permissions.
- */
- if (mode & S_IRUSR)
- buf[1] = 'r';
- if (mode & S_IWUSR)
- buf[2] = 'w';
- if (mode & S_IXUSR)
- buf[3] = 'x';
- if (mode & S_IRGRP)
- buf[4] = 'r';
- if (mode & S_IWGRP)
- buf[5] = 'w';
- if (mode & S_IXGRP)
- buf[6] = 'x';
- if (mode & S_IROTH)
- buf[7] = 'r';
- if (mode & S_IWOTH)
- buf[8] = 'w';
- if (mode & S_IXOTH)
- buf[9] = 'x';
-
- /*
- * Finally fill in magic stuff like suid and sticky text.
- */
- if (mode & S_ISUID)
- buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
- if (mode & S_ISGID)
- buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
- if (mode & S_ISVTX)
- buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
-
- return buf;
-}
-
-
-/*
- * 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(time_t timeVal)
-{
- time_t now;
- char * str;
- static char buf[26];
-
- time(&now);
-
- str = ctime(&timeVal);
-
- strcpy(buf, &str[4]);
- buf[12] = '\0';
-
- if ((timeVal > now) || (timeVal < now - 365*24*60*60L))
- {
- strcpy(&buf[7], &str[20]);
- buf[11] = '\0';
- }
-
- return buf;
-}
-
-
-/*
- * 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
-isDevice(const char * name)
-{
- struct stat statBuf;
-
- if (stat(name, &statBuf) < 0)
- return FALSE;
-
- return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode);
-}
-
-
-/*
- * Copy one file to another, while possibly preserving its modes, times,
- * and modes. Returns TRUE if successful, or FALSE on a failure with an
- * error message output. (Failure is not indicted if the attributes cannot
- * be set.)
- */
-BOOL
-copyFile(
- const char * srcName,
- const char * destName,
- BOOL setModes
-)
-{
- int rfd;
- int wfd;
- int rcc;
- char buf[BUF_SIZE];
- struct stat statBuf1;
- struct stat statBuf2;
- struct utimbuf times;
-
- if (stat(srcName, &statBuf1) < 0)
- {
- perror(srcName);
-
- return FALSE;
- }
-
- 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))
- {
- fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
-
- return FALSE;
- }
-
- rfd = open(srcName, O_RDONLY);
-
- if (rfd < 0)
- {
- perror(srcName);
-
- return FALSE;
- }
-
- wfd = creat(destName, statBuf1.st_mode);
-
- if (wfd < 0)
- {
- perror(destName);
- close(rfd);
-
- return FALSE;
- }
-
- while ((rcc = read(rfd, buf, sizeof(buf))) > 0)
- {
- if (intFlag)
- {
- close(rfd);
- close(wfd);
-
- return FALSE;
- }
-
- if (fullWrite(wfd, buf, rcc) < 0)
- goto error_exit;
- }
-
- if (rcc < 0)
- {
- perror(srcName);
- goto error_exit;
- }
-
- checkStatus("close", close(rfd));
-
- if (close(wfd) < 0)
- {
- perror(destName);
-
- return FALSE;
- }
-
- if (setModes)
- {
- checkStatus("chmod", chmod(destName, statBuf1.st_mode));
-
- checkStatus("chown", chown(destName, statBuf1.st_uid, statBuf1.st_gid));
-
- times.actime = statBuf1.st_atime;
- times.modtime = statBuf1.st_mtime;
-
- checkStatus("utime", utime(destName, &times));
- }
-
- return TRUE;
-
-
-error_exit:
- close(rfd);
- close(wfd);
-
- return FALSE;
-}
-
-
-/*
- * Build a path name from the specified directory name and file name.
- * If the directory name is NULL, then the original fileName is returned.
- * The built path is in a static area, and is overwritten for each call.
- */
-const char *
-buildName(const char * dirName, const char * fileName)
-{
- const char * cp;
- static char buf[PATH_LEN];
-
- if ((dirName == NULL) || (*dirName == '\0'))
- return fileName;
-
- cp = strrchr(fileName, '/');
-
- if (cp)
- fileName = cp + 1;
-
- strcpy(buf, dirName);
- strcat(buf, "/");
- strcat(buf, fileName);
-
- return buf;
-}
-
-
-/*
- * 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. 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(const char * fileNamePattern, const char *** retFileTable)
-{
- const char * last;
- const char * cp1;
- const char * cp2;
- const char * cp3;
- char * str;
- DIR * dirp;
- struct dirent * dp;
- int dirLen;
- int newFileTableSize;
- char ** newFileTable;
- char dirName[PATH_LEN];
-
- static int fileCount;
- static int fileTableSize;
- static char ** fileTable;
-
- /*
- * Clear the return values until we know their final values.
- */
- fileCount = 0;
- *retFileTable = NULL;
-
- /*
- * 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 file name component\n");
-
- return -1;
- }
-
- /*
- * Assume at first that we are scanning the current directory.
- */
- dirName[0] = '.';
- dirName[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';
- }
- }
-
- /*
- * Open the directory containing the files to be checked.
- */
- dirp = opendir(dirName);
-
- if (dirp == NULL)
- {
- perror(dirName);
-
- return -1;
- }
-
- /*
- * Prepare the directory name for use in making full path names.
- */
- dirLen = strlen(dirName);
-
- if (last == fileNamePattern)
- {
- dirLen = 0;
- dirName[0] = '\0';
- }
- else if (dirName[dirLen - 1] != '/')
- {
- dirName[dirLen++] = '/';
- dirName[dirLen] = '\0';
- }
-
- /*
- * 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;
-
- /*
- * 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;
-
- 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;
- }
-
- /*
- * 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 file name\n");
- closedir(dirp);
-
- return -1;
- }
-
- /*
- * Save the file name in the chunk.
- */
- if (dirLen)
- memcpy(str, dirName, dirLen);
-
- strcpy(str + dirLen, dp->d_name);
-
- /*
- * Save the allocated file name into the file table.
- */
- fileTable[fileCount++] = str;
- }
-
- /*
- * Close the directory and check for any matches.
- */
- closedir(dirp);
-
- if (fileCount == 0)
- {
- fprintf(stderr, "No matches\n");
-
- return -1;
- }
-
- /*
- * Sort the list of file names.
- */
- qsort((void *) fileTable, fileCount, sizeof(char *), nameSort);
-
- /*
- * Return the file list and count.
- */
- *retFileTable = (const char **) fileTable;
-
- return fileCount;
-}
-
-
-/*
- * Sort routine for list of fileNames.
- */
-int
-nameSort(const void * p1, const void * p2)
-{
- const char ** s1;
- const char ** s2;
-
- s1 = (const char **) p1;
- s2 = (const char **) p2;
-
- return strcmp(*s1, *s2);
-}
-
-
-/*
- * Routine to see if a text string is matched by a wildcard pattern.
- * Returns TRUE if the text is matched, or FALSE if it is not matched
- * or if the pattern is invalid.
- * * matches zero or more characters
- * ? matches a single character
- * [abc] matches 'a', 'b' or 'c'
- * \c quotes character c
- * Adapted from code written by Ingo Wilken.
- */
-BOOL
-match(const char * text, const char * pattern)
-{
- const char * retryPat;
- const char * retryText;
- int ch;
- BOOL found;
-
- retryPat = NULL;
- retryText = NULL;
-
- while (*text || *pattern)
- {
- ch = *pattern++;
-
- switch (ch)
- {
- case '*':
- retryPat = pattern;
- retryText = text;
- break;
-
- case '[':
- found = FALSE;
-
- while ((ch = *pattern++) != ']')
- {
- if (ch == '\\')
- ch = *pattern++;
-
- if (ch == '\0')
- return FALSE;
-
- if (*text == ch)
- found = TRUE;
- }
-
- if (!found)
- {
- pattern = retryPat;
- text = ++retryText;
- }
-
- /* fall into next case */
-
- case '?':
- if (*text++ == '\0')
- return FALSE;
-
- break;
-
- case '\\':
- ch = *pattern++;
-
- if (ch == '\0')
- return FALSE;
-
- /* fall into next case */
-
- default:
- if (*text == ch)
- {
- if (*text)
- text++;
- break;
- }
-
- if (*text)
- {
- pattern = retryPat;
- text = ++retryText;
- break;
- }
-
- return FALSE;
- }
-
- if (pattern == NULL)
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-/*
- * 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(const char * cmd, int * retArgc, const char *** retArgv)
-{
- const char * argument;
- char * cp;
- 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;
-
- /*
- * Clear the returned values until we know them.
- */
- 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;
-
- if (len > stringsLength)
- {
- newStrings = realloc(strings, len);
-
- if (newStrings == NULL)
- {
- fprintf(stderr, "Cannot allocate string\n");
-
- return FALSE;
- }
-
- 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;
- }
-
- /*
- * 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))
- *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;
- }
-
- /*
- * Null terminate the argument list and return it.
- */
- argTable[argCount] = NULL;
-
- *retArgc = argCount;
- *retArgv = argTable;
-
- return TRUE;
-}
-
-
-/*
- * Make a NULL-terminated string out of an argc, argv pair.
- * Returns TRUE if successful, or FALSE if the string is too long,
- * with an error message given. This does not handle spaces within
- * arguments correctly.
- */
-BOOL
-makeString(
- int argc,
- const char ** argv,
- char * buf,
- int bufLen
-)
-{
- int len;
-
- while (argc-- > 0)
- {
- len = strlen(*argv);
-
- if (len >= bufLen)
- {
- fprintf(stderr, "Argument string too long\n");
-
- return FALSE;
- }
-
- strcpy(buf, *argv++);
-
- buf += len;
- bufLen -= len;
-
- if (argc)
- *buf++ = ' ';
-
- bufLen--;
- }
-
- *buf = '\0';
-
- return TRUE;
-}
-
-
-/*
- * Allocate a chunk of memory (like malloc).
- * The difference, though, is that the memory allocated is put on a
- * list of chunks which can be freed all at one time. You CAN NOT free
- * an individual chunk.
- */
-char *
-getChunk(int size)
-{
- CHUNK * chunk;
-
- if (size < CHUNK_INIT_SIZE)
- size = CHUNK_INIT_SIZE;
-
- chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE);
-
- if (chunk == NULL)
- return NULL;
-
- chunk->next = chunkList;
- chunkList = chunk;
-
- return chunk->data;
-}
-
-
-/*
- * 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.
- */
-char *
-chunkstrdup(const char * str)
-{
- int len;
- char * newStr;
-
- len = strlen(str) + 1;
- newStr = getChunk(len);
-
- if (newStr)
- memcpy(newStr, str, len);
-
- return newStr;
-}
-
-
-/*
- * Free all chunks of memory that had been allocated since the last
- * call to this routine.
- */
-void
-freeChunks(void)
-{
- CHUNK * chunk;
-
- while (chunkList)
- {
- chunk = chunkList;
- chunkList = chunk->next;
- free((char *) chunk);
- }
-}
-
-
-/*
- * Try writing data to the specified file descriptor.
- * Only the first write error if any is printed.
- * This is used when writing to STDOUT.
- */
-void
-tryWrite(int fd, const char * cp, int len)
-{
- static int failed = FALSE;
-
- int status = fullWrite(fd, cp, len);
-
- if ((status < 0) && !failed)
- {
- failed = TRUE;
- perror("write");
- }
-}
-
-
-/*
- * Write all of the supplied buffer out to a file.
- * This does multiple writes as necessary.
- * Returns the amount written, or -1 on an error.
- */
-int
-fullWrite(int fd, const char * buf, int len)
-{
- int cc;
- int total;
-
- total = 0;
-
- while (len > 0)
- {
- cc = write(fd, buf, len);
-
- if (cc < 0)
- return -1;
-
- buf += cc;
- total+= cc;
- len -= cc;
- }
-
- 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;
-}
-
-
-/*
- * Call system for the specified command and print an error
- * message if the execution fails. The exit status of the
- * command is returned.
- */
-int
-trySystem(const char * cmd)
-{
- int status;
-
- status = system(cmd);
-
- if (status == -1)
- fprintf(stderr, "Error starting command: %s\n", cmd);
-
- return status;
-}
-
-
-/*
- * Check the status for the most recent system call and complain
- * if its value is -1 which indicates it failed.
- */
-void
-checkStatus(const char * name, int status)
-{
- if (status == -1)
- perror(name);
-}
-
-/* END CODE */