jac@yoko.rutgers.edu (Jonathan A. Chandross) (05/02/91)
Submitted-by: Andy McFadden (fadden@cory.berkeley.edu) Posting-number: Volume 1, Source:34 Archive-name: archive/unix/nulib/part03.10 Architecture: UNIX Version-number: 3.03 =nuread.c -/* - * nuread.c - read NuFX archives (header info only) into structures - * - * NuLib v3.0 February 1991 Freeware (distribute, don't sell) - * By Andy McFadden (fadden@cory.berkeley.edu) - */ -#ifdef APW -segment "NuMain" -#endif - -#include "nudefs.h" -#include <stdio.h> -#include <fcntl.h> -#include <errno.h> - -#ifdef MSDOS /* For file IO */ -# include <stdlib.h> /* errno, among others */ -# include <string.h> -# include <io.h> -# include <sys/types.h> -# include <sys/stat.h> -#endif - -#ifdef CRC_TAB -# include "crc.h" /* fast CRC lookup */ -#endif -#include "nuread.h" -#include "nupak.h" /* uses PAKBUFSIZ */ -#include "nuetc.h" - -#define UNKNOWN_FN "<No Filename>" - -/* quick proc to save x00 bytes of static storage */ -void OtherArc(str1, str2) -{ - fprintf(stderr, "File may be %s; try \"%s\".\n", str1, str2); -} - -/* swap two bytes if HiLo is TRUE */ -void HiSwap(ptr, a, b) -onebyt *ptr; -register onebyt a, b; -{ - register onebyt tmp; - - if (HiLo) { - tmp = ptr[a], ptr[a] = ptr[b], ptr[b] = tmp; - } -} - - -/* copy bytes from buffer to buffer, reversing byte order if necessary */ -void BCopy(srcptr, destptr, num, order) -onebyt *srcptr, *destptr; -register int num; -BOOLEAN order; /* true if byte ordering is important */ -{ - register int i = num--; - - if (order && HiLo) { - while (i--) { /* copy & reverse */ - *(destptr+i) = *(srcptr + num - i); /* dest+3 = src + 3 - 3 .. */ - } - } else if (order) { - while (i--) { /* copy only */ - *(destptr+i) = *(srcptr + i); - } - } else { - while (i--) { /* byte ordering not important; just copy */ - *(destptr+i) = *(srcptr+i); - } - } -} - - -/* - * Calculate CRC on a region - * - * A CRC is the result of a mathematical operation based on the - * coefficients of a polynomial when multiplied by X^16 then divided by - * the generator polynomial (X^16 + X^12 + X^5 + 1) using modulo two - * arithmetic. - * - * This routine is a slightly modified verison of one found in: - * _Advanced Programming Techniques for the Apple //gs Toolbox_ - * By Morgan Davis and Dan Gookin (Compute! Publications, Inc.) - * It can either calculate the CRC bit-by-bit or use a table. - * [ one of the few //gs books worth the money +atm ] - */ -twobyt CalcCRC(seed, ptr, count) -twobyt seed; /* initial value for CRC */ -onebyt *ptr; /* pointer to start of data buffer */ -int count; /* number of bytes to scan through - note 64K max */ -{ - register int x; - register twobyt CRC = seed; - - do { -#ifndef CRC_TAB - CRC ^= *ptr++ << 8; /* XOR hi-byte of CRC w/data */ - for (x = 8; x; --x) /* Then, for 8 bit shifts... */ - if (CRC & 0x8000) /* Test hi order bit of CRC */ - CRC = CRC << 1 ^ 0x1021; /* if set, shift & XOR w/$1021 */ - else - CRC <<= 1; /* Else, just shift left once. */ -#else - CRC = updcrc(*ptr++, CRC); /* look up new value in table */ -#endif - } while (--count); - return (CRC); -} - - -/* - * Test an archive's integrity. - * - * Reads the entire file, and checks CRCs for certain things. - */ -void NuTest(filename, options) -char *filename; -char *options; -{ - ListHdr *archive; - onebyt *copybuf; /* buffer for reading record */ - int partial; /* size for partial read */ - unsigned int rec; - RNode *RNodePtr; - MHblock *MHptr; - TNode *TNodePtr; - long hdr_size, total_size, thread_size; - int srcfd; /* file descriptor */ - int thread; - twobyt CRC, RecordCRC; - long CRCsum = 0L; /* sum of CRCs for all records */ - BOOLEAN check_thread_crc; /* TRUE if we want to check a give thread */ - static char *procName = "NuTest"; - - printf("Testing %s", filename); - if (verbose) printf("\n"); - else { printf("..."); fflush(stdout); } - - archive = NuRead(filename); /* this catches most errors... */ - - MHptr = archive->MHptr; - RNodePtr = archive->RNodePtr; - copybuf = (onebyt *) Malloc(PAKBUFSIZ); - if ((srcfd = open(filename, O_RDONLY | O_BINARY)) < 0) - Fatal("Unable to close archive", procName); - if (lseek(srcfd, (long) MHsize, S_ABS) < 0) /* seek past master block */ - Fatal("Bad seek (MH)", procName); - - for (rec = 0; rec < (unsigned int) MHptr->total_records; rec++) { - if (verbose) printf("Record %d (%s): ", rec, RNodePtr->filename); - hdr_size = (long) RNodePtr->RHptr->attrib_count; - hdr_size += (long) RNodePtr->filename_length; - total_size = hdr_size; - TNodePtr = RNodePtr->TNodePtr; - for (thread=0; thread < (int)RNodePtr->RHptr->total_threads; thread++){ - if (TNodePtr == (TNode *) NULL) { - fprintf(stderr, "Internal error: Bad thread structure\n"); - Quit(-1); - } - hdr_size += (long) THsize; - total_size += (long) THsize; - total_size += TNodePtr->THptr->comp_thread_eof; - TNodePtr = TNodePtr->TNext; - } - if (verbose) { - printf("total record size = %ld (%d threads)\n", total_size, - (int) RNodePtr->RHptr->total_threads); - fflush(stdout); - } - - /* read record header */ - RecordCRC = 0; - while (hdr_size != 0L) { - if (hdr_size > (long) PAKBUFSIZ) { - partial = (unsigned int) PAKBUFSIZ; - hdr_size -= (long) PAKBUFSIZ; - } else { - partial = (unsigned int) hdr_size; - hdr_size = 0L; - } - - if (read(srcfd, copybuf, partial) != partial) { - fprintf(stderr, ">>> Read error"); - if (verbose) fprintf(stderr, "\n"); - else fprintf(stderr, - " - record %d (%s)\n", rec, RNodePtr->filename); - fprintf(stderr, "Operation aborted.\n"); - Quit(-1); - } - if (verbose) RecordCRC = CalcCRC(CRC, (onebyt *) copybuf, partial); - } - - TNodePtr = RNodePtr->TNodePtr; - for (thread=0; thread < (int)RNodePtr->RHptr->total_threads; thread++){ - if (lseek(srcfd, (long) TNodePtr->fileposn, S_ABS) < 0) - Fatal("whoops!", procName); - thread_size = TNodePtr->THptr->comp_thread_eof; - - /* decide whether or not to check thread CRCs */ - check_thread_crc = FALSE; - if (RNodePtr->RHptr->version_number >= 2) /* valid CRCs */ - if (TNodePtr->THptr->thread_class == 2) /* data_thread */ - check_thread_crc = TRUE; - if (RNodePtr->RHptr->version_number == 3) /* CRC of uncom data */ - if (TNodePtr->THptr->thread_format != 0x0000) - check_thread_crc = FALSE; /* can't check comp */ - - if (check_thread_crc) CRC = 0xffff; - while (thread_size != 0L) { - if (thread_size > (long) PAKBUFSIZ) { - partial = (unsigned int) PAKBUFSIZ; - thread_size -= (long) PAKBUFSIZ; - } else { - partial = (unsigned int) thread_size; - thread_size = 0L; - } - - if (read(srcfd, copybuf, partial) != partial) { - fprintf(stderr, ">>> Read error in thread"); - if (verbose) fprintf(stderr, " %d\n", thread); - else fprintf(stderr, " - record %d (%s), thread %d\n", - rec, RNodePtr->filename, thread); - fprintf(stderr, "Operation aborted.\n"); - Quit(-1); - } - - if (verbose) - RecordCRC = CalcCRC(RecordCRC, (onebyt *)copybuf, partial); - - /* calculate CRC for thread, and compare with thread_crc */ - if (check_thread_crc) - CRC = CalcCRC(CRC, (onebyt *) copybuf, partial); -#ifdef DEBUG - printf( -"At posn %ld: rec %d/thread %d (%ld bytes) CalcCRC = 0x%.4x (0x%.4x)\n", -TNodePtr->fileposn, rec, thread, thread_size, CRC, TNodePtr->THptr->thread_crc - ); -#endif - } - - /* check and see if CRC matches */ - if (check_thread_crc) { - if (CRC != TNodePtr->THptr->thread_crc) { - fprintf(stderr, ">>> Bad CRC for thread %d", - thread); - if (verbose) fprintf(stderr, "\n"); - else fprintf(stderr, " in record %d\n", rec); - } else { - if (verbose) printf("--- CRC matched for thread %d\n", - thread); - } - } - TNodePtr = TNodePtr->TNext; - } - - if (verbose) { - printf("--- CRC for entire record was $%.4x\n", RecordCRC); - CRCsum += RecordCRC; - } - RNodePtr = RNodePtr->RNext; - } - if (close(srcfd) < 0) - Fatal("Unable to close archive", procName); - - free(copybuf); - if (verbose) printf("Sum of CRCs = $%.8lx\n", CRCsum); - printf("done.\n"); -} - - -/* - * Read thread header data, and skip data fields - */ - -static TNode *ReadThreads(fd, RHptr, RNodePtr, CRC_ptr) -int fd; -RHblock *RHptr; -RNode *RNodePtr; -twobyt *CRC_ptr; /* CRC seed; result is returned thru this */ -{ - int i; - unsigned int size; - BOOLEAN first; - TNode *TNodePtr, *THeadPtr = (TNode *) NULL; - THblock *THptr; - char filebuf[THsize]; - twobyt CRC = *CRC_ptr; - static char *procName = "ReadThreads"; - - RNodePtr->unc_len = 0L; - RNodePtr->comp_len = 0L; - first = TRUE; - for (i = 0; i < RHptr->total_threads; i++) { - if (first) { /* create first block, or... */ - TNodePtr = (TNode *) Malloc(sizeof(TNode)); - THeadPtr = TNodePtr; - first = FALSE; - } else { /* create next block and go on */ - TNodePtr->TNext = (TNode *) Malloc(sizeof(TNode)); - TNodePtr = TNodePtr->TNext; - } - TNodePtr->TNext = (TNode *) NULL; - - /* Create the thread header block, and read it in */ - TNodePtr->THptr = (THblock *) Malloc(sizeof(THblock)); - THptr = TNodePtr->THptr; - - if (size = read(fd, filebuf, THsize) < THsize) { /* should be 16 */ - printf("read size = %d, THsize = %d\n", size, THsize); - Fatal("ReadThread (THblock)", procName); - } - CRC = CalcCRC(CRC, (onebyt *) filebuf, 16); /* hdr CRC part(s) 5/5 */ - - /* copy all fields... */ - BCopy(filebuf+0, (onebyt *) &THptr->thread_class, 2, TRUE); - BCopy(filebuf+2, (onebyt *) &THptr->thread_format, 2, TRUE); - BCopy(filebuf+4, (onebyt *) &THptr->thread_kind, 2, TRUE); - BCopy(filebuf+6, (onebyt *) &THptr->thread_crc, 2, TRUE); - BCopy(filebuf+8, (onebyt *) &THptr->thread_eof, 4, TRUE); - BCopy(filebuf+12, (onebyt *) &THptr->comp_thread_eof, 4, TRUE); - - RNodePtr->unc_len += THptr->thread_eof; - RNodePtr->comp_len += THptr->comp_thread_eof; - if (THptr->comp_thread_eof > 2097152L) /* SANITY CHECK */ - fprintf(stderr, "Sanity check: found comp_thread_eof > 2MB\n"); - } - - /* skip the actual data */ - TNodePtr = THeadPtr; - for (i = 0; i < RHptr->total_threads; i++) { - THptr = TNodePtr->THptr; - if ((TNodePtr->fileposn = lseek(fd, 0L, S_REL)) < 0) - Fatal("Bad thread posn lseek()", procName); - - /* pull filenames out of threads, if present */ - if (THptr->thread_class == 0x0003) { /* filename thread */ - RNodePtr->filename = (char *) Malloc(THptr->thread_eof +1); - if (read(fd, RNodePtr->filename, THptr->thread_eof) < 0) { - fprintf(stderr, "Error on thread %d\n", i); - Fatal("Unable to read filename", procName); - } - RNodePtr->filename[THptr->thread_eof] = '\0'; - RNodePtr->real_fn_length = THptr->thread_eof; - - { /* patch to fix bug in ShrinkIt v3.0.0 */ - int j, name_len = strlen(RNodePtr->filename); - - for (j = 0; j < name_len; j++) { - RNodePtr->filename[j] &= 0x7f; /* clear hi bit */ - } - } - - if (lseek(fd, TNodePtr->fileposn, S_ABS) < 0) - Fatal("Unable to seek back after fn", procName); - } - if (lseek(fd, THptr->comp_thread_eof, S_REL) < 0) - Fatal("Bad skip-thread seek", procName); - TNodePtr = TNodePtr->TNext; - } - *CRC_ptr = CRC; - return (THeadPtr); -} - - -/* - * Read header data from a NuFX archive into memory - */ -ListHdr *NuRead(filename) -char *filename; -{ - int fd; /* archive file descriptor */ - char namebuf[MAXFILENAME]; - int rec, num; - BOOLEAN first; - twobyt namelen; - twobyt CRC; - ListHdr *ListPtr; /* List Header struct */ - MHblock *MHptr; /* Master Header block */ - RNode *RNodePtr; /* Record Node */ - RHblock *RHptr; /* Record Header block */ - onebyt filebuf[RECBUFSIZ]; /* must be > RH, MH, or atts-RH size */ - static char *procName = "NuRead"; - - ListPtr = (ListHdr *) Malloc(sizeof(ListHdr)); /* create head of list */ - ListPtr->arc_name = (char *) Malloc(strlen(filename)+1); /* archive fnam */ - strcpy(ListPtr->arc_name, filename); - ListPtr->MHptr = (MHblock *) Malloc(sizeof(MHblock)); /* master block */ - - if ((fd = open(filename, O_RDONLY | O_BINARY)) < 0) { - if (errno == ENOENT) { - fprintf(stderr, "%s: can't find file '%s'\n", prgName, filename); - Quit (-1); - } else - Fatal("Unable to open archive", procName); - } - - /* create and read the master header block */ - MHptr = ListPtr->MHptr; - if (read(fd, filebuf, MHsize) < MHsize) { - fprintf(stderr, "File '%s' may not be a NuFX archive\n", filename); - Fatal("Unable to read Master Header Block", procName); - } - - CRC = CalcCRC(0, (onebyt *) filebuf+8, MHsize-8); /* master header CRC */ - - /* Copy data to structs, correcting byte ordering if necessary */ - BCopy(filebuf+0, (onebyt *) MHptr->ID, 6, FALSE); - BCopy(filebuf+6, (onebyt *) &MHptr->master_crc, 2, TRUE); - BCopy(filebuf+8, (onebyt *) &MHptr->total_records, 4, TRUE); - BCopy(filebuf+12, (onebyt *) &MHptr->arc_create_when, sizeof(Time), FALSE); - BCopy(filebuf+20, (onebyt *) &MHptr->arc_mod_when, sizeof(Time), FALSE); - BCopy(filebuf+28, (onebyt *) &MHptr->master_version, 2, TRUE); - BCopy(filebuf+30, (onebyt *) MHptr->reserved1, 8, FALSE); - BCopy(filebuf+38, (onebyt *) &MHptr->master_eof, 4, TRUE); /* m_v $0001 */ - BCopy(filebuf+42, (onebyt *) MHptr->reserved2, 6, FALSE); - - if (strncmp(MHptr->ID, MasterID, 6)) { - fprintf(stderr, "\nFile '%s' is not a NuFX archive\n", filename); - if ((filebuf[0] == 10) && (filebuf[1] == 71) && - (filebuf[2] == 76) && (filebuf[18] == 2)) -#ifdef NO_BLU - OtherArc("Binary II", "unblu"); -#else - fprintf(stderr, "File may be Binary II; try 'B' option\n"); -#endif - if ((filebuf[0] == '\037') && (filebuf[1] == '\036')) - OtherArc("packed", "unpack"); - if ((filebuf[0] == (onebyt)'\377') && (filebuf[1] == '\037')) - OtherArc("compacted", "uncompact"); - if ((filebuf[0] == '\037') && (filebuf[1] == (onebyt)'\235')) - OtherArc("compressed", "uncompress"); - if ((filebuf[0] == 0x76) && (filebuf[1] == 0xff)) - OtherArc("SQueezed", "usq"); - if ((filebuf[0] == 0x04) && (filebuf[1] == 0x03) && - (filebuf[2] == 0x4b) && (filebuf[3] == 0x50)) - OtherArc("a ZIP archive", "UnZip"); - if (!strncmp((char *) filebuf, "ZOO", 3)) /* zoo */ - OtherArc("a ZOO archive", "zoo"); - if ((filebuf[0] == 0x1a) && (filebuf[1] == 0x08)) /* arc */ - OtherArc("an ARC archive", "arc"); - if (!strncmp((char *) filebuf, "SIT!", 4)) /* StuffIt */ - OtherArc("a StuffIt archive", "StuffIt (Macintosh)"); - if (!strncmp((char *) filebuf, "<ar>", 4)) /* system V arch */ - OtherArc("a library archive (Sys V)", "ar"); - if (!strncmp((char *) filebuf, "!<arch>", 7)) - OtherArc("a library archive", "ar"); - if (!strncmp((char *) filebuf, "#! /bin/sh", 10) || - !strncmp((char *) filebuf, "#!/bin/sh", 9)) - OtherArc("a shar archive", "/bin/sh"); - if (!strncmp((char *) filebuf, "GIF87a", 6)) - OtherArc("a GIF picture", "?!?"); - /* still need ZIP */ - - Quit (-1); - } - - if (CRC != MHptr->master_crc) - printf("WARNING: Master Header block may be corrupted (bad CRC)\n"); - - if (MHptr->master_version > MAXMVERS) - printf("WARNING: unknown Master Header version, trying to continue\n"); - - /* main record read loop */ - first = TRUE; - for (rec = 0; rec < (unsigned int) MHptr->total_records; rec++) { - if (first) { /* allocate first, or... */ - ListPtr->RNodePtr = (RNode *) Malloc(sizeof(RNode)); - RNodePtr = ListPtr->RNodePtr; - first = FALSE; - } else { /* allocate next, and go on */ - RNodePtr->RNext = (RNode *) Malloc(sizeof(RNode)); /* next Rnode */ - RNodePtr = RNodePtr->RNext; /* move on to next record */ - } - RNodePtr->RNext = (RNode *) NULL; - - RNodePtr->RHptr = (RHblock *) Malloc(sizeof(RHblock)); /* alloc blk */ - /* expansion here */ - RHptr = RNodePtr->RHptr; - if (read(fd, filebuf, RHsize) < RHsize) { /* get known stuff */ - fprintf(stderr,"%s: error in record %d (at EOF?)\n", prgName, rec); - Fatal("Bad RHblock read", procName); - } - - /* rec hdr CRC part 1/5 */ - CRC = CalcCRC(0, (onebyt *) filebuf+6, RHsize-6); - - BCopy(filebuf+0, (onebyt *) RHptr->ID, 4, FALSE); - BCopy(filebuf+4, (onebyt *) &RHptr->header_crc, 2, TRUE); - BCopy(filebuf+6, (onebyt *) &RHptr->attrib_count, 2, TRUE); - BCopy(filebuf+8, (onebyt *) &RHptr->version_number, 2, TRUE); - BCopy(filebuf+10, (onebyt *) &RHptr->total_threads, 2, TRUE); - BCopy(filebuf+12, (onebyt *) &RHptr->reserved1, 2, TRUE); - BCopy(filebuf+14, (onebyt *) &RHptr->file_sys_id, 2, TRUE); - BCopy(filebuf+16, (onebyt *) &RHptr->file_sys_info, 1, TRUE); - BCopy(filebuf+17, (onebyt *) &RHptr->reserved2, 1, TRUE); - BCopy(filebuf+18, (onebyt *) &RHptr->access, 4, TRUE); - BCopy(filebuf+22, (onebyt *) &RHptr->file_type, 4, TRUE); - BCopy(filebuf+26, (onebyt *) &RHptr->extra_type, 4, TRUE); - BCopy(filebuf+30, (onebyt *) &RHptr->storage_type, 2, TRUE); - BCopy(filebuf+32, (onebyt *) &RHptr->create_when, sizeof(Time), FALSE); - BCopy(filebuf+40, (onebyt *) &RHptr->mod_when, sizeof(Time), FALSE); - BCopy(filebuf+48, (onebyt *) &RHptr->archive_when, sizeof(Time), FALSE); - BCopy(filebuf+56, (onebyt *) &RHptr->option_size, 2, TRUE); - /* expansion here */ - - if (strncmp(RHptr->ID, RecordID, 4)) { - fprintf(stderr, "%s: Found bad record ID (#%d) -- exiting\n", - prgName, rec); - Quit (-1); - } - - /* read remaining (unknown) attributes into buffer, if any */ - num = RHptr->attrib_count - RHsize - 2; - if (num > RECBUFSIZ) { - fprintf(stderr, "ERROR: attrib_count > RECBUFSIZ\n"); - Quit (-1); - } - if (num > 0) { - if (read(fd, filebuf, num) < num) - Fatal("Bad xtra attr read", procName); - CRC = CalcCRC(CRC, (onebyt *) filebuf, num); /* hdr CRC part 2/5 */ - } - - if (read(fd, (char *) &namelen, 2) < 2) /* read filename len */ - Fatal("Bad namelen read", procName); - CRC = CalcCRC(CRC, (onebyt *) &namelen, 2); /* rec hdr CRC part 3/5 */ - - HiSwap((onebyt *) &namelen, 0, 1); - /* read filename, and store in struct */ - if (namelen > MAXFILENAME) { - fprintf(stderr, "ERROR: namelen > MAXFILENAME\n"); - Quit (-1); - } - RNodePtr->filename_length = namelen; - - if (namelen > 0) { - RNodePtr->real_fn_length = namelen; - if (read(fd, namebuf, namelen) < namelen) - Fatal("Bad namebuf read", procName); - /* rec hdr CRC part 4/5 */ - CRC = CalcCRC(CRC, (onebyt *) namebuf, namelen); - - RNodePtr->filename = (char *) Malloc(namelen+1); /* store fname */ - BCopy(namebuf, (onebyt *) RNodePtr->filename, namelen, FALSE); - RNodePtr->filename[namelen] = '\0'; - } else { - RNodePtr->filename = UNKNOWN_FN; - RNodePtr->real_fn_length = strlen(UNKNOWN_FN); - } - - RNodePtr->TNodePtr = ReadThreads(fd, RHptr, RNodePtr, &CRC); - /* rec hdr CRC part 5/5 calculated by ReadThreads */ - - if (CRC != RHptr->header_crc) { - printf("WARNING: Detected a bad record header CRC\n"); - printf(" Rec %d in file '%.60s'\n",rec,RNodePtr->filename); - } - } - - /* begin adding new files at this point */ - if ((ListPtr->nextposn = lseek(fd, 0L, S_REL)) < 0) - Fatal("Bad final lseek()", procName); - - if (close(fd) < 0) { - Fatal("Bad close", procName); - } - - if (MHptr->master_version > 0x0000) { - if (MHptr->master_eof != ListPtr->nextposn) { - printf("WARNING: master_eof (stored)=%ld, nextposn (actual)=%ld\n", - MHptr->master_eof, ListPtr->nextposn); - printf( - " (master_eof will be fixed if archive is changed)\n"); - } - } - - return (ListPtr); -} - =nuext.c -/* - * nuext.c - operations which extract from a NuFX archive - * - * NuLib v3.0 February 1991 Freeware (distribute, don't sell) - * By Andy McFadden (fadden@cory.berkeley.edu) - */ -#ifdef APW -segment "NuMain" -#endif - -#include "nudefs.h" -#include <stdio.h> -#ifdef BSD43 -# include <strings.h> -#else /* SYSV, APW, MSC */ -# include <string.h> -#endif -#include <fcntl.h> - -#ifdef UNIX -# include <errno.h> -# include <time.h> -# include <sys/types.h> -# include <sys/stat.h> -#endif -#ifdef APW -# include <types.h> -# include <prodos.h> -# include <shell.h> -# include <strings.h> -#endif -#ifdef MSDOS -# include <io.h> -# include <time.h> -# include <stdlib.h> -# include <errno.h> -# include <direct.h> -# include <utime.h> -# include <sys/types.h> -# include <sys/stat.h> -#endif - -#include "nuread.h" -#include "nuext.h" -#include "nupak.h" -#include "nuetc.h" - -static BOOLEAN extall; /* extract all files? */ -static BOOLEAN print; /* extract to screen rather than file? */ - - -/* - * Get the answer to a yes/no question. - * - * Returns TRUE for yes, FALSE for no. May return additional things in the - * future... (y/n/q)? - */ -int AskYesNo() -{ - char buf[16]; /* if user answers with >16 chars, bad things happen */ - char c; - - printf(" (y/n)? "); - fflush(stdout); - gets(buf); - c = *buf; - if ((c == 'y') || (c == 'Y')) - return (TRUE); - else - return (FALSE); -} - - -/* - * Convert a filename to one legal in the present file system. - * - * Does not allocate new space; alters string in place (so original string - * will be "corrupted"). Assumes that it has been passed a filename without - * the filename separators. - */ -void ConvFileName(str) -char *str; -{ - int idx = 0; -#ifdef UNIX - - while (*str != '\0') { - if ((*str > 127) || (*str < 0)) *str &= 0x7f; /* clear hi bit */ - if (*str == '/') *str = '.'; - if (++idx > 255) { *str = '\0'; break; } - str++; - } -#else -# ifdef APW - static char *legal = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789."; - - /* assumes ProDOS limits, not GS/OS */ - if ( ((*str < 'A') && (*str > 'Z')) || ((*str < 'a') && (*str > 'z')) ) - *str = 'X'; /* must start with alpha char */ - while (*str != '\0') { - if (!INDEX(legal, *str)) *str = '.'; - if (++idx > 15) { *str = '\0'; break; } - str++; - } -# endif /* APW */ -# ifdef MSDOS - while (*str != '\0') { - if ((*str > 127) || (*str < 0)) *str &= 0x7f; /* clear hi bit */ - if (*str == '/') *str = '_'; - if (*str == '\\') *str = '_'; - if (*str == '!') *str = '_'; - if (*str == ':') *str = '_'; - if (++idx > 255) { *str = '\0'; break; } - str++; - } -# endif /* MSDOS */ - -# ifndef APW -# ifndef MSDOS - printf("Need [other] filename converter\n"); /* +PORT+ */ -# endif /* none2 */ -# endif /* none1 */ -#endif /*UNIX*/ -} - -/* - * Set a file's attributes according to info in a record structure. - */ -void SetFInfo(filename, RHptr) -char *filename; -RHblock *RHptr; -{ - static char *procName = "SetFInfo"; -#ifdef UNIX - long ltime; - time_t timep[2]; - - ltime = ReduceTime(&RHptr->mod_when); /* set both to mod time */ - timep[0] = ltime; /* accessed */ - timep[1] = ltime; /* modified */ - utime(filename, timep); - - if ((RHptr->access == 0xe3L) || (RHptr->access == 0xc3L)) /* unlocked */ - chmod(filename, S_IREAD | S_IWRITE | 044); - if ((RHptr->access == 0x21L) || (RHptr->access == 0x01L)) /* locked */ - chmod(filename, S_IREAD | 044); - -#else /* UNIX */ -# ifdef APW - /* - * Call ProDOS SET_FILE_INFO to set attributes for a file. - * Uses the information in the record header block. - */ - FileRec finfo; - OpenRec oinfo; - twobyt date, time; - long ltime; - - finfo.pathname = c2pstr(filename); /* temp storage...? */ - finfo.fAccess = (twobyt) RHptr->access; - finfo.fileType = (twobyt) RHptr->file_type; - finfo.auxType = RHptr->extra_type; - finfo.storageType = 0; /* RHptr->storage_type otherwise */ - ltime = ReduceTime(&RHptr->create_when); - date = (twobyt) ltime; /* date is lower 16 */ - time = (twobyt) (ltime >> 16); /* time is upper */ - finfo.createDate = date; - finfo.createTime = time; - ltime = ReduceTime(&RHptr->mod_when); - date = (twobyt) ltime; /* date is lower 16 */ - time = (twobyt) (ltime >> 16); /* time is upper */ - finfo.modDate = date; - finfo.modTime = time; - - SET_FILE_INFO( &finfo ); - ToolErrChk(); -# endif /* APW */ -# ifdef MSDOS - long ltime; - time_t timep[2]; - - ltime = ReduceTime(&RHptr->mod_when); - timep[0] = ltime; /* accessed */ - timep[1] = ltime; /* modified */ - utime(filename, timep); - - if ((RHptr->access == 0xe3L) || (RHptr->access == 0xc3L)) /* unlocked */ - chmod(filename, S_IREAD | S_IWRITE | 044); - if ((RHptr->access == 0x21L) || (RHptr->access == 0x01L)) /* locked */ - chmod(filename, S_IREAD | 044); -# endif /* MSDOS */ - -# ifndef APW -# ifndef MSDOS - printf("need [other] SetFInfo stuff\n"); /* +PORT+ */ -# endif /* none2 */ -# endif /* none1 */ -#endif /* APW */ -} - - -/* - * Create a subdirectory - * - * This routine will exit on most errors, since generally more than one file - * will be unpacked to a given subdirectory, and we don't want it charging - * bravely onward if it's going to run into the same problem every time. - */ -void CreateSubdir(pathname) -char *pathname; -{ - char *buffer = (char *) Malloc(MAXFILENAME+6); - static char *procName = "CreateSubdir"; -#ifdef UNIX - struct stat st; - - /* if no directory exists, then make one */ - if (stat(pathname, &st) < 0) - if (errno == ENOENT) { - sprintf(buffer, "mkdir %s", pathname); - if (system(buffer) != 0) /* call UNIX mkdir to create subdir */ - Fatal("Unable to create subdir", procName); - } else { - Fatal("Unable to create dir", procName); - } -#else -# ifdef APW - static FileRec create_p = { "", 0x00e3, 0x000f, 0L, 0x000d, 0, 0 }; /*dir*/ - FileRec info_p; /* check if file exists, is dir */ - int err; /* holds _toolErr */ - - strcpy(buffer, pathname); - c2pstr(buffer); - info_p.pathname = buffer; - GET_FILE_INFO( &info_p ); - - switch (_toolErr) { - case 0x0000: /* no error */ - if (info_p.storageType != 0x000d) /* not a DIR? */ - Fatal("File in path exists, is not a directory.", procName); - return; /* file exists, is directory, no need to create */ - - case fileNotFound: - create_p.pathname = buffer; - CREATE( &create_p ); - if (!_toolErr) return; /* created okay? */ - else ToolErrChk(); - - default: /* unknown error */ - ToolErrChk(); - Fatal("whoops!", procName); /* shouldn't get here */ - } -# endif /* APW */ -# ifdef MSDOS - struct stat st; - - /* if no directory exists, then make one */ - if (stat(pathname, &st) < 0) - if (errno == ENOENT) { - if (mkdir(pathname) != 0) - Fatal("Unable to create subdir", procName); - } else { - Fatal("Unable to create dir", procName); - } -# endif /* MSDOS */ - -# ifndef APW -# ifndef MSDOS - - /* don't forget to check if it exists first... */ /* +PORT+ */ - printf("don't know how to create [other] subdirectories\n"); /* mkdir() */ -# endif /* none2 */ -# endif /* none1 */ -#endif /* UNIX */ - free(buffer); -} - - -/* - * Given a pathname, create subdirectories as needed. All file names are run - * through a system-dependent filename filter, which means that the pathname - * has to be broken down, the subdirectory created, and then the pathname - * reconstructed with the "legal" pathname. The converted filename is held - * in a static buffer; subsequent calls will overwrite the previous string. - * - * This is useful when unpacking "dir1/dir2/fubar" and dir1 and dir2 don't - * necessarily exist. - * - * It is assumed that all filenames are relative to the current directory. - * According to the NuFX docs (revision 3 2/3/89), initial separators (like - * "/", "\", or ":") should NOT be included. If they are, this routine may - * break. - */ -static char *CreatePath(pathname, fssep) -char *pathname; /* full pathname; should not include ProDOS volume name */ -onebyt fssep; /* file system pathname separator, usually "/" or "\" */ -{ - int idx; - char *ptr; - static char workbuf[MAXFILENAME]; /* work buffer; must be static */ - static char *procName = "CreatePath"; - - idx = 0; - while (TRUE) { /* move through string */ - ptr = INDEX(pathname, fssep); /* find break */ - if (ptr) /* down to actual filename? */ - *ptr = '\0'; /* no, isolate this part of the string */ - - strcpy(&workbuf[idx], pathname); /* copy component to buf */ - ConvFileName(&workbuf[idx]); /* convert to legal str; may be shorter */ - idx += strlen(&workbuf[idx]); /* advance index to end of string */ - if (!ptr) { /* down to actual filename? */ - workbuf[idx] = '\0'; /* yes, clean up */ - break; /* out of while */ - } - workbuf[idx] = '\0'; - - CreateSubdir(workbuf); /* system-dependent dir create */ - -#ifdef UNIX - workbuf[idx++] = '/'; /* tack a filename separator on, and advance */ - *ptr = '/'; /* be nice */ -#else -# ifdef APW - workbuf[idx++] = '/'; - *ptr = '/'; -# endif -# ifdef MSDOS - workbuf[idx++] = '\'; - *ptr = '\'; -# endif -# ifndef APW /* +PORT+ */ -# ifndef MSDOS - workbuf[idx++] = '/'; - *ptr = '/'; -# endif -# endif -#endif /*UNIX*/ - -/* was: workbuf[idx++] = fssep; /* tack an fssep on the end, and advance */ -/* was: *ptr = fssep; /* be nice */ - - pathname = ptr+1; /* go again with next component */ - } - - return (workbuf); -} - - -/* - * Extract a thread, and place in a file. - * - * Returns TRUE if the extract was successful, FALSE otherwise. The most - * common reason for a FALSE return value is a "no" answer when asked about - * overwriting an existing file. - */ -static BOOLEAN ExtractThread(arcfd, fileposn, destpn, THptr) -int arcfd; /* source file descriptor (must be open) */ -long fileposn; /* position of data in source file */ -char *destpn; /* destination filename */ -THblock *THptr; /* pointer to thread info */ -{ - int dstfd; /* destination file descriptor */ - static char *procName = "ExtractThread"; - - if (!print) { - if (Exists(destpn)) { - if (interact) { - if (verbose) printf("file exists, overwite"); - else printf("%s exists, overwite", destpn); - if (!AskYesNo()) { /* return w/o overwriting */ - return (FALSE); - } - } - if (verbose) { printf("overwriting..."); fflush(stdout); } - if (unlink(destpn) < 0) - Fatal("Unable to remove existing file", procName); - } - - if ((dstfd = - open(destpn, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, WPERMS)) < 0) - Fatal("Unable to open target path", procName); - - if (lseek(arcfd, fileposn, S_ABS) < 0) - Fatal("Seek failed", procName); - - if (!UnpackFile(arcfd, dstfd, THptr, - dopack ? THptr->thread_format : 0, pakbuf)) { - if (close(dstfd) < 0) - Fatal("Dest close failed", procName); - unlink(destpn); /* some sys can't delete while file open */ - } else { - if (close(dstfd) < 0) - Fatal("Dest close failed", procName); - } - - } else { /* print */ - if ((dstfd = fileno(stdout)) < 0) - Fatal("Unable to get file for stdout", procName); - if (lseek(arcfd, fileposn, S_ABS) < 0) - Fatal("Seek failed", procName); - - if (!UnpackFile(arcfd, dstfd, THptr, - dopack ? THptr->thread_format : 0, pakbuf)) { - printf("Unpack failed.\n"); - return (FALSE); - } - fflush(stdout); - } - - return (TRUE); -} - - -/* - * Handle message_threads - */ -static void message_thread(arcfd, RNodePtr, TNodePtr) -int arcfd; -RNode *RNodePtr; -TNode *TNodePtr; -{ - int i; - int oldTo, oldFrom; - static char *procName = "message_thread"; - - switch (TNodePtr->THptr->thread_kind) { - case 0x0000: /* ASCII text */ - printf("Found ASCII text thread\n"); - break; - case 0x0001: /* ASCII text, predefined size */ - if (verbose && !print && TNodePtr->THptr->thread_eof) { - printf("\n--- Comment for file '%s':\n", RNodePtr->filename); - fflush(stdout); - if (lseek(arcfd, TNodePtr->fileposn, S_ABS) < 0) - Fatal("unable to seek to comment", procName); - oldTo = transto; - oldFrom = transfrom; - transto = -1; /* switch to CR -> current mode */ - transfrom = 0; /* (assumes created under ProDOS) */ - /* may need to fix this later (but how?) */ - FCopy(arcfd, fileno(stdout), TNodePtr->THptr->thread_eof, - pakbuf, TRUE); -#ifdef FUBAR - print = TRUE; - verbose = FALSE; /* turn off "unshrinking..." messages */ - ExtractThread(arcfd,TNodePtr->fileposn, "stdout", TNodePtr->THptr); - print = FALSE; - verbose = TRUE; -#endif - transto = oldTo; - transfrom = oldFrom; - putchar('\n'); - } - break; - default: - printf("Found unknown message_thread %.4x in '%s'\n", - TNodePtr->THptr->thread_kind, RNodePtr->filename); - break; - } -} - -/* - * Handle control_threads - */ -static void control_thread(arcfd, RNodePtr, TNodePtr) -int arcfd; -RNode *RNodePtr; -TNode *TNodePtr; -{ - switch (TNodePtr->THptr->thread_kind) { - case 0x000: /* create dir */ - printf("Found create directory control thread\n"); - break; - default: - printf("Found unknown control_thread %.4x in '%s'\n", - TNodePtr->THptr->thread_kind, RNodePtr->filename); - break; - } -} - - -/* - * Handle data_threads - * - * Does not guarantee that the archive file position is anything rational; - * the TNode's fileposn should be (and is) used here. - */ -static void data_thread(arcfd, RNodePtr, TNodePtr) -int arcfd; -RNode *RNodePtr; -TNode *TNodePtr; -{ - long fileposn; /* absolute position of thread in file */ - long old_eof; - char *fn; - int ov; - - if (print) /* this is something of a hack... */ - if (TNodePtr->THptr->thread_kind != 0x0000) { /* not a data fork? */ - fprintf(stderr, "Can't print non-data fork for '%s'.\n", - RNodePtr->filename); - return; /* this hoses the file posn... */ - } else { - if (verbose) printf("\n***** %s *****\n", RNodePtr->filename); - fflush(stdout); - ov = verbose; - verbose = FALSE; /* turn off "unshrinking..." messages */ - fileposn = TNodePtr->fileposn; - ExtractThread(arcfd,fileposn, "stdout", TNodePtr->THptr); - verbose = ov; - return; - } - - switch (TNodePtr->THptr->thread_kind) { - case 0x0000: /* data fork */ - if (verbose) { - printf("Extracting '%s' (data)...", RNodePtr->filename); - fflush(stdout); - } - - /* create any needed subdirs */ - fn = CreatePath(RNodePtr->filename, RNodePtr->RHptr->file_sys_info); - - /* extract the file */ - if (ExtractThread(arcfd, TNodePtr->fileposn, fn, TNodePtr->THptr)) { - SetFInfo(fn, RNodePtr->RHptr); /* set file attributes, dates... */ - if (verbose) printf("done.\n"); - } - break; - - case 0x0001: /* disk image */ -/* printf("Found disk image (not extracted)\n");*/ - - if (verbose) { - printf("Extracting '%s' (disk image)...", RNodePtr->filename); - fflush(stdout); - } - - /* setup path (normalize file name) */ - fn = CreatePath(RNodePtr->filename, RNodePtr->RHptr->file_sys_info); - - /* thread_eof is invalid for disks, so figure it out */ - old_eof = TNodePtr->THptr->thread_eof; - if (RNodePtr->RHptr->storage_type <= 3) { /* should be block */ - TNodePtr->THptr->thread_eof = /* size, but shk301 */ - RNodePtr->RHptr->extra_type * 512; /* stored it wrong */ - } else { - TNodePtr->THptr->thread_eof = - RNodePtr->RHptr->extra_type * RNodePtr->RHptr->storage_type; - } - - /* extract the disk into a file */ - if (ExtractThread(arcfd, TNodePtr->fileposn, fn, TNodePtr->THptr)) { - /*SetFInfo(fn, RNodePtr->RHptr);/* set file attributes, dates... */ - if (verbose) printf("done.\n"); - } - - TNodePtr->THptr->thread_eof = old_eof; - break; - - case 0x0002: /* resource_fork */ - printf("Found resource_fork (not extracted)\n"); - break; - default: - printf("Found unknown data_thread %.4x in '%s'\n", - TNodePtr->THptr->thread_kind, RNodePtr->filename); - break; - } -} - - -/* - * Extract files from archive - * - * Scan archive, extracting files which start with the strings in "names". - * Calls subroutines to handle the various thread_class types. - */ -static void Extract(filename, namecount, names) -char *filename; -int namecount; -char **names; -{ - ListHdr *archive; - int arcfd; /* archive file descriptor */ - int rec, idx; - MHblock *MHptr; /* Master Header block */ - RNode *RNodePtr; /* Record Node */ - TNode *TNodePtr; /* Thread block */ - int len, *lentab; /* hold strlen() of all names */ - char *pn; /* archived pathname */ - int thread; /* current thread #; max 65535 threads */ - BOOLEAN gotone = FALSE; - static char *procName = "Extract"; - - archive = NuRead(filename); - if ((arcfd = open(archive->arc_name, O_RDONLY | O_BINARY)) < 0) - Fatal("Unable to open archive", procName); - - pakbuf = (onebyt *) Malloc(PAKBUFSIZ); /* allocate unpack buffer */ - - lentab = (int *) Malloc( sizeof(int) * namecount ); /* calloc() is nicer */ - for (idx = 0; idx < namecount; idx++) /* calc. once (for efficiency) */ - lentab[idx] = strlen(names[idx]); - - MHptr = archive->MHptr; - RNodePtr = archive->RNodePtr; - - if (!namecount) - extall = TRUE; - - /* main record read loop */ - for (rec = 0; rec < MHptr->total_records; rec++) { - pn = RNodePtr->filename; - len = strlen(pn); - if (RNodePtr->RHptr->version_number > MAXVERS) { - printf("Unable to extract '%s': unknown record version_number\n", - pn); - continue; /* with for */ - } - - for (idx = 0; extall || idx < namecount; idx++) { /* find arced file */ - /* try to match argument with first few chars of stored filename */ - /* or the entire filename, depending on EXPAND flag */ - if (extall || ((len >= lentab[idx]) && doExpand ? - (!strncasecmp(pn, names[idx], lentab[idx])) : - (!strcasecmp(pn, names[idx])) )) { - - gotone = TRUE; - /* go through all threads */ - TNodePtr = RNodePtr->TNodePtr; - for (thread = 0; thread < (int) RNodePtr->RHptr->total_threads; - thread++) { - switch(TNodePtr->THptr->thread_class) { - case 0x0000: - message_thread(arcfd, RNodePtr, TNodePtr); - break; - case 0x0001: - control_thread(arcfd, RNodePtr, TNodePtr); - break; - case 0x0002: - /* don't extract if doMessages is set */ - if (!doMessages) - data_thread(arcfd, RNodePtr, TNodePtr); - break; - case 0x0003: - /* filename_thread; ignore */ - break; - default: - printf("Unknown thread_class %.4x for '%s'\n", - TNodePtr->THptr->thread_class, RNodePtr->filename); - break; - } - TNodePtr = TNodePtr->TNext; - } - break; /* out of filename matching (inner) FOR loop */ - } - } - - RNodePtr = RNodePtr->RNext; /* move on to next record */ - } - if (!gotone && verbose) - printf("None selected\n"); - if (close(arcfd) < 0) - Fatal("Source (archive) close failed", procName); - -} - -/* - * Entry point to extract routines. - */ -void NuExtract(filename, namecount, names, options) -char *filename; -int namecount; -char **names; -char *options; -{ - static char *procName = "NuExtract"; - - if (*options == 'p') { /* printing rather then extracting to file */ - print = TRUE; - dopack = TRUE; /* no extract uncompressed! */ - } else print = FALSE; - - Extract(filename, namecount, names); /* do stuff */ -} - + END OF ARCHIVE