diff options
Diffstat (limited to 'src/sash')
-rw-r--r-- | src/sash/CHANGES | 31 | ||||
-rw-r--r-- | src/sash/Makefile | 69 | ||||
-rw-r--r-- | src/sash/README | 17 | ||||
-rw-r--r-- | src/sash/cmd_ar.c | 1019 | ||||
-rw-r--r-- | src/sash/cmd_chattr.c | 265 | ||||
-rw-r--r-- | src/sash/cmd_dd.c | 372 | ||||
-rw-r--r-- | src/sash/cmd_ed.c | 1440 | ||||
-rw-r--r-- | src/sash/cmd_file.c | 235 | ||||
-rw-r--r-- | src/sash/cmd_find.c | 376 | ||||
-rw-r--r-- | src/sash/cmd_grep.c | 197 | ||||
-rw-r--r-- | src/sash/cmd_gzip.c | 698 | ||||
-rw-r--r-- | src/sash/cmd_ls.c | 597 | ||||
-rw-r--r-- | src/sash/cmd_tar.c | 1277 | ||||
-rw-r--r-- | src/sash/cmds.c | 1562 | ||||
-rw-r--r-- | src/sash/dirent.h | 10 | ||||
-rw-r--r-- | src/sash/sash.1 | 591 | ||||
-rw-r--r-- | src/sash/sash.c | 1373 | ||||
-rw-r--r-- | src/sash/sash.h | 169 | ||||
-rw-r--r-- | src/sash/utils.c | 1144 |
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, ×)); - } - - 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 */ |