diff options
Diffstat (limited to 'src/sash/cmd_ar.c')
-rw-r--r-- | src/sash/cmd_ar.c | 1019 |
1 files changed, 0 insertions, 1019 deletions
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 */ |