Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!ucbvax!pasteur!cory.Berkeley.EDU!fadden From: fadden@cory.Berkeley.EDU (Andy McFadden) Newsgroups: comp.binaries.apple2 Subject: NuLib v2.1.1 NuFX archiver (UNIX/APW) shar format 3/5 Message-ID: <19359@pasteur.Berkeley.EDU> Date: 9 Nov 89 09:20:57 GMT Sender: news@pasteur.Berkeley.EDU Reply-To: fadden@cory.Berkeley.EDU (Andy McFadden) Organization: University of California, Berkeley Lines: 1509 NuLib part 3/5 ----- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # nuext.c # numain.c # nupak.c # This archive created: Thu Nov 9 01:07:30 1989 # By: Andy McFadden () export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'nuext.c'" '(16679 characters)' if test -f 'nuext.c' then echo shar: "will not over-write existing file 'nuext.c'" else cat << \!Funky!Stuff! > 'nuext.c' /* * nuext.c - operations which extract from a NuFX archive * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include #ifdef BSD43 # include #else /* SYSV, APW, MSC */ # include #endif #include #ifdef UNIX # include # include # include # include #endif #ifdef APW # include # include # include # include #endif #ifdef MSDOS # include # include # include # include # include # include # include # include #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); c = *gets(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 attirbutes 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 */ workbuf[idx++] = fssep; /* tack an fssep on the end, and advance */ *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->comp_thread_eof, THptr->thread_eof, 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->comp_thread_eof, THptr->thread_eof, 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; { switch (TNodePtr->THptr->thread_kind) { case 0x000: /* ASCII text */ printf("Found ASCII text thread\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 */ 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); ov = verbose; verbose = FALSE; /* turn off "unshrinking..." messages */ fileposn = RNodePtr->TNodePtr->fileposn; ExtractThread(arcfd,fileposn, "stdout", RNodePtr->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 */ fileposn = RNodePtr->TNodePtr->fileposn; if (ExtractThread(arcfd, fileposn, fn, RNodePtr->TNodePtr->THptr)) { SetFInfo(fn, RNodePtr->RHptr); /* set file attributes, dates... */ if (verbose) printf("done.\n"); } break; case 0x0001: /* disk image */ printf("Found disk image\n"); break; case 0x0002: /* resource_fork */ printf("Found resource_fork\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: data_thread(arcfd, RNodePtr, TNodePtr); 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 */ } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'numain.c'" '(14251 characters)' if test -f 'numain.c' then echo shar: "will not over-write existing file 'numain.c'" else cat << \!Funky!Stuff! > 'numain.c' /* * numain.c - shell-based front end for CShrink * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" /* system-dependent defines */ #include /* standard I/O library */ #include #include #include /* C type stuff, like tolower() */ #ifdef BSD43 # include #else /* SYSV, APW, MSC */ # include /* string stuff */ #endif #ifdef APW # include # include # include # include #endif #include "nuread.h" /* structs for archive info, archive read routines */ #include "nuview.h" /* archive listing functions */ #include "nuadd.h" /* archive operations (add, create, move) */ #include "nuext.h" /* archive operations (extract) */ #include "nupdel.h" /* archive operations (delete, update, freshen) */ #include "nublu.h" /* Binary II archive operations */ #include "nupak.h" /* need PAKBUFSIZ */ #include "nuetc.h" /* Malloc(), Fatal(), etc. */ extern char *getenv(); /* +PORT+ */ #define Whoops(str) printf("WARNING: typedef %s may be set incorrectly\n",str); #define ENVAR "NULIBOPT" /* environment variable with options in it */ /* * global to entire program */ int HiLo; /* byte ordering; FALSE on low-first (65816) */ int verbose; /* print verbose? */ int interact; /* interactive overwrite mode? */ int dopack; /* do we want to pack/unpack? */ int doExpand; /* do we want to expand archive filenames? */ int doSubdir; /* process subdirectories at all? */ int transfrom; /* how to do CR<->LF translation (from what?) (-1 = off) */ int transto; /* translate to ? */ int packMethod; /* how to pack a file */ fourbyt defFileType; /* default file type */ fourbyt defAuxType; /* default aux type */ onebyt *pakbuf; /* used by compression routines; created once to reduce */ /* overhead involved in malloc()ing a 64K buffer */ char *prgName = "NuLib"; /* for error messages; don't like argv[0] */ /* besides, the name changes every 3 weeks */ static char *header = "CShrink v2.1.1 November 1989 Freeware Copyright 1989 By Andy McFadden"; /* * Print simple usage info */ static void Usage(argv0) char *argv0; { printf("\nUsage: %s option[suboption] archive-name [filespec]\n", argv0); printf("\nType \"%s h\" for help.\n", argv0); } /* * Print usage info */ static void Help(argv0, options) char *argv0, *options; { if (INDEX(options+1, 'n')) { /* using 'n' suboption? */ printf("%s\n", header); printf("\nCompression methods:\n"); printf(" # Name Abbr Pack? Unpack?\n"); printf(" 0: Uncompressed unc Y Y\n"); printf(" 1: SQueezed (sq/usq) squ N Y\n"); printf(" 2: Dynamic LZW (ShrinkIt) shk * Y\n"); printf("The default is #2\n"); printf("\nText conversion methods:\n"); printf(" 0: From/to CR (ProDOS files)\n"); printf(" 1: From/to LF (UNIX files)\n"); printf(" 2: From/to CRLF (MS-DOS files)\n"); } else if (INDEX(options+1, 'w')) { /* print author info */ printf("%s\n", header); printf( "Internet: fadden@cory.berkeley.edu Usenet: ...!ucbvax!cory!fadden\n"); printf("\nShrinkIt and NuFX standard by Andy Nicholas.\n"); printf("ShrinkIt LZW compression by Kent Dickey\n"); printf( "Binary II unpack and unsqueeze C code adapted from unblu.c and usq.c by\n"); printf(" Marcel J.E. Mol (usq.c based on usq2/sq3 by Don Elton).\n"); printf("\nMS-DOS port by Robert B. Hess (roberth@microsof.uu.net).\n"); printf( "\nThanks go to all those who helped me test this, especially to the few\n"); printf(" who bothered to respond.\n"); printf( "\nThis program is Freeware. Please distribute as widely as possible, but\n"); printf( " don't sell it. Source code is available via e-mail upon request.\n"); printf( "\nUsers without Usenet/Internet access may send mail to me at:\n"); printf(" 1474 Saskatchewan Drive\n"); printf(" Sunnyvale, CA 94087\n"); } else { /* default help screen */ printf("%s\n", header); printf( "\nUsage: %s option[suboption] archive-name [filespec]\n", argv0); printf("Option must be one of:\n"); printf(" a[vucsrf] add to archive\n"); #ifndef NO_BLU printf(" b[xvti] Binary II archive operations\n"); #endif printf( " c[vucsrf] create archive (add, but suppress 'create' message)\n"); printf(" d[v+] delete file from archive\n"); printf(" f[vucsrf] freshen files in archive\n"); printf(" h[nw] show help screen\n"); printf(" i[v] verify archive integrity\n"); printf(" m[vucsrf] move files to archive (add, delete original)\n"); printf(" p[vt+] print archived file to stdout\n"); printf(" t[vz] display table of contents\n"); printf(" u[vucsrf] update files in archive\n"); printf(" v verbose listing (ProDOS 8 ShrinkIt format)\n"); printf(" x,e[vuti+] extract from archive\n"); printf("[ ShrinkIt compression partially implemented ]\n"); } } /* * Check machine dependencies */ static void CheckMach() { onebyt one; onebyt *oneptr; twobyt two; fourbyt four; #ifdef UNIX # ifdef APW ^^ "ERROR: You have both APW and UNIX defined" ^^ # endif # ifdef MSDOS ^^ "ERROR: You have both MSDOS and UNIX defined" ^^ # endif #endif /*UNIX*/ #ifdef APW # ifdef MSDOS ^^ "ERROR: You have both APW and MSDOS defined" ^^ # endif #endif /* some compilers complain about (unsigned) -1 , so I'm doing it this */ /* way to keep everybody quiet. */ one = 0x100; if (one) Whoops("onebyt"); /* one > 1 */ two = 0x10000; if (two) Whoops("twobyt"); /* two > 2 */ two = 0x1000; if (!two) Whoops("twobyt"); /* two < 2 */ four = 0xffffffff; four++; if (four) Whoops("fourbyt"); /* four > 4 */ four = 0x10000; if (!four) Whoops("fourbyt"); /* four < 4 */ /* check byte ordering */ two = 0x1122; oneptr = (onebyt *) &two; if (*oneptr == 0x11) HiLo = TRUE; else if (*oneptr == 0x22) HiLo = FALSE; else { printf("WARNING: Unable to determine a value for HiLo\n"); HiLo = FALSE; } /* check some other stuff... compilers may (should?) give warnings here */ if (ATTSIZE < MHsize) printf("WARNING: ATTSIZE must be >= than MHsize\n"); if (RECBUFSIZ < ATTSIZE) printf("WARNING: RECBUFSIZ should be larger than ATTSIZE\n"); if (MHsize != 48 || THsize != 16) printf("WARNING: Bad MHsize or THsize\n"); if (sizeof(Time) != 8) printf("WARNING: struct Time not 8 bytes\n"); } /* * Check to see if string 2 is in string 1. * * Returns the position of string 2 within string 1; -1 if not found. */ static int strc(host, sub) char *host, *sub; { int hlen = strlen(host); int slen = strlen(sub); int i; if (slen > hlen) /* substring longer than host string */ return (-1); /* generic linear search... */ for (i = 0; i <= (hlen - slen); i++) if ((*(host+i) == *sub) && (!strncmp(host+i, sub, slen))) return (i); return (-1); } /* * Yank a number from a character string. */ int OptNum(ptr) char *ptr; { int val = 0; while (*ptr && isdigit(*ptr)) { val *= 10; val += (*ptr - '0'); ptr++; } return (val); } /* * Set default values for globals. * * Should be of form "NULIBOPT=..." * verbose : default to verbose output * interactive : default to interactive mode when overwriting * type=xxx : set storage type to ProDOS type "xxx" * aux=xxxx : set aux storage type to 4-byte hex number "xxxx" */ void GetDefaults(options) char *options; { char *envptr; int off, idx, pt; int len = strlen(options); char type[5]; /* set program default values */ verbose = FALSE; /* silent mode */ interact = FALSE; /* don't ask questions */ doSubdir = TRUE; /* process subdirectories unless told not to */ dopack = TRUE; /* don't pack unless told to */ doExpand = FALSE; /* don't expand archived filenames */ packMethod = 0x0002;/* ShrinkIt LZW */ transfrom = -1; /* no text translation */ transto = -1; defFileType = (fourbyt) 0; /* NON */ defAuxType = (fourbyt) 0; /* $0000 */ /* read from global envir var */ if (envptr = getenv(ENVAR)) { if (strc(envptr, "verbose") >= 0) { verbose = TRUE; } if (strc(envptr, "interactive") >= 0) { interact = TRUE; } if ((off = strc(envptr, "type=")) >= 0) { off += 5; if (off+3 > strlen(envptr)) { fprintf(stderr, "Error with 'type=xxx' in NULIBOPT var\n"); Quit (-1); } strncpy(type, envptr+off, 3); type[3] = '\0'; for (idx = 0; idx < 256; idx++) /* scan for file type */ if (!strcasecmp(FT[idx], type)) { defFileType = (fourbyt) idx; break; /* out of for */ } } if ((off = strc(envptr, "aux=")) >= 0) { off += 4; if (off+4 > strlen(envptr)) { fprintf(stderr, "Error with 'aux=$xxxx' in NULIBOPT var\n"); Quit (-1); } strncpy(type, envptr+off, 4); type[4] = '\0'; sscanf(type, "%x", &defAuxType); } } /* handle command line suboption string */ for (pt = 1; pt < len; pt++) { /* skip option char */ switch(options[pt]) { case '+': /* expand */ doExpand = TRUE; break; case 'c': /* compress method */ packMethod = OptNum(&options[pt+1]); while (pt < len && isdigit(options[pt+1])) /* advance to next */ pt++; dopack = TRUE; break; case 'f': /* filetype specified */ strncpy(type, &options[pt+1], 3); type[3] = '\0'; for (idx = 0; idx < 256; idx++) /* scan for file type */ if (!strcasecmp(FT[idx], type)) { defFileType = (fourbyt) idx; break; /* out of for */ } pt += strlen(type); if (options[pt+1] == '/') { /* auxtype specification */ pt++; strncpy(type, &options[pt+1], 4); type[4] = '\0'; sscanf(type, "%lx", &defAuxType); pt += strlen(type); } break; case 'i': /* interactive overwrites */ interact = TRUE; break; case 'n': /* help with numbers */ /* do nothing */ break; case 'r': /* don't recursively descend subdir */ doSubdir = FALSE; break; case 's': /* store method */ packMethod = OptNum(&options[pt+1]); while (pt < len && isdigit(options[pt+1])) /* advance to next */ pt++; dopack = FALSE; break; case 't': /* how to translate text? */ transfrom = OptNum(&options[pt+1]); while (pt < len && isdigit(options[pt+1])) pt++; break; case 'u': /* don't use compression */ dopack = FALSE; /* this doesn't matter, but FALSE may be faster */ packMethod = 0x0000; /* archive w/o compression */ break; case 'v': /* verbose mode */ verbose = TRUE; break; case 'w': /* help on people */ /* do nothing */ break; case 'x': /* extract BLU files */ /* do nothing */ break; default: /* unknown */ printf("unknown subopt '%c'\n", options[pt]); break; /* do nothing */ } } /* for */ } #ifdef APW /* * Expand a ProDOS filename using APW wildcard calls (even if the file doesn't * exist). * * Returns a pointer to a buffer holding the filename. */ char *ExpandFilename(filename) char *filename; { char *ptr; c2pstr(filename); if (!(*filename)) { printf("Internal error: can't expand null filename\n"); Quit (-1); } INIT_WILDCARD(filename, 0); ToolErrChk(); p2cstr(filename); NEXT_WILDCARD(tmpNameBuf); p2cstr(tmpNameBuf); if (strlen(tmpNameBuf)) /* found it */ return(tmpNameBuf); else { /* file does not exist; expand path */ strcpy(tmpNameBuf, filename); ptr = RINDEX(tmpNameBuf, '/'); /* remove filename */ if (!ptr) /* filename only */ return (filename); *ptr = '\0'; if (!strlen(tmpNameBuf)) { /* something weird... */ printf("Unable to expand '%s'\n", filename); Quit (-1); } c2pstr(tmpNameBuf); INIT_WILDCARD(tmpNameBuf, 0); ToolErrChk(); NEXT_WILDCARD(tmpNameBuf); p2cstr(tmpNameBuf); if (!strlen(tmpNameBuf)) { printf("Unable to fully expand '%s'\n", filename); Quit (-1); } strcat(tmpNameBuf, RINDEX(filename, '/')); return (tmpNameBuf); } } #endif /* APW */ /* * Parse args, call functions. */ main(argc, argv) int argc; char **argv; { char *filename; /* hold expanded archive file name */ int idx; filename = (char *) Malloc(MAXFILENAME); CheckMach(); /* check compiler options, and set HiLo */ if (argc < 2) { /* no arguments supplied */ Usage(argv[0]); Quit (0); } if (argc < 3) { /* no archive file specified; show help screen */ if (argv[1][0] == 'h') /* could be HN or HW */ Help(argv[0], argv[1]); else /* not 'H' option; show generic help scrn */ Help(argv[0], "h"); Quit (0); } #ifdef APW strcpy(filename, ExpandFilename(argv[2])); #else strcpy(filename, argv[2]); #endif if (argv[1][0] == '-') { /* skip initial dashes */ argv[1]++; } for (idx = 0; argv[1][idx]; idx++) /* conv opts to lower case */ if (isupper(argv[1][idx])) argv[1][idx] = tolower(argv[1][idx]); GetDefaults(argv[1]); /* get defaults, process suboption string */ pakbuf = (onebyt *) Malloc(PAKBUFSIZ); /* allocate global pack buf */ switch (argv[1][0]) { case 'a': /* add */ case 'c': /* create */ case 'm': /* move */ NuAdd(filename, argc-3, argv+3, argv[1]); /* NuAdd will read */ break; #ifndef NO_BLU case 'b': /* Binary II operations */ NuBNY(filename, argc-3, argv+3, argv[1]); break; #endif case 'd': /* delete */ NuDelete(filename, argc-3, argv+3, argv[1]); break; case 'f': /* freshen */ case 'u': /* update */ NuUpdate(filename, argc-3, argv+3, argv[1]); break; case 'i': /* verify integrity */ NuTest(filename, argv[1]); break; case 't': /* table of contents */ case 'v': /* verbose output */ NuView(filename, argv[1]); break; case 'e': /* extract */ case 'x': case 'p': NuExtract(filename, argc-3, argv+3, argv[1]); break; default: /* need help */ fprintf(stderr, "%s: unknown option '%c'\n", argv[0], argv[1][0]); break; } free (filename); free (pakbuf); Quit (0); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nupak.c'" '(10049 characters)' if test -f 'nupak.c' then echo shar: "will not over-write existing file 'nupak.c'" else cat << \!Funky!Stuff! > 'nupak.c' /* * nupak.c - interface to the compression routines * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 Novmber 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include #include #ifdef MSDOS /* for file I/O */ # include # include # include # include #endif #include "nupak.h" #include "nuetc.h" #define CONVSIZ 1024 long packedSize; /* global - size of file after packing */ onebyt lastseen; /* used in crlf(); must be set by caller */ /* * Make a little spinning thing. * * This just lets the user know that the whole thing hasn't stalled on him. * Prints a character, then backspaces so that the next thing will overwrite * it. * * Currently called by FCopy(), unpak_SHK(), pak_SHK() */ void Spin() { static char *sp = "/-\\|"; static int posn = 0; posn++; if ((posn < 0) || (posn > 3)) posn = 0; putchar(sp[posn]); putchar('\b'); fflush(stdout); } /* * Convert the end-of-line terminator between systems. * * Compile-time defines determine the value that things are translated to; * the value of "translate" determines what they are translated from. This * will write the contents of the buffer to the passed file descriptor, * altering bytes as necessary. Max buffer size is 64K. Note that the * syntax is the same as for write(); * * This would have been a lot easier without IBM... lastseen is the last * character seen (used only in CRLF translations). This needs to be set * by the caller (FCopy(), extract_files()). * * The putc_ncr() procedure in nusq.c does its own processing; this was * somewhat unavoidable. * * BUGS: This proc will have to be re-written. It would be nice to be * able to pack files with a CRLF translation, not just unpack... but you * can't just do buffer writes for that. It'll take some work, and will * probably appear in the next version. */ unsigned int crlf(dstfd, buffer, length) int dstfd; onebyt *buffer; unsigned int length; { register BOOLEAN doconv; register onebyt *bptr = buffer; register unsigned int idx; static char *procName = "crlf"; unsigned int partial; /* size for partial read/write */ onebyt tobuf[2048]; onebyt *toptr; int conv; unsigned int origlength = length; if ((transfrom == -1) && (transto == -1)) { /* no translation necessary */ return (write(dstfd, buffer, length)); } if (transfrom < -1 || transfrom > 2) { fprintf(stderr, "%s: unknown translation type %d\n", prgName, transfrom); fprintf(stderr, "%s: assuming conversion 0 (from CR)\n", prgName); transfrom = 0; } if (transto < -1 || transto > 2) { fprintf(stderr, "%s: unknown translation type %d\n", prgName, transto); fprintf(stderr, "%s: assuming conversion 0 (to CR)\n", prgName); transto = 0; } /* macro defs for system-dependent actions */ #ifdef UNIX # define DEFCONVFROM if (*bptr == 0x0a) /* LF */ \ doconv = TRUE # define DEFCONVTO *(toptr++) = 0x0a #else # ifdef APW # define DEFCONVFROM if (*bptr == 0x0d) /* CR */ \ doconv = TRUE # define DEFCONVTO *(toptr++) = 0x0d # endif # ifdef MSDOS # define DEFCONVFROM if ((*bptr == 0x0a) && (lastseen == 0x0d)) { \ doconv = TRUE; toptr--; /*already outputed CR; back up over it*/ } lastseen = *bptr # define DEFCONVTO *(toptr++) = 0x0d; \ *(toptr++) = 0x0a # endif # ifndef APW # ifndef MSDOS # define DEFCONVFROM if (*bptr == 0x0a) /* LF */ \ doconv = TRUE # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ while (length != 0) { if (length > CONVSIZ) { partial = CONVSIZ; length -= CONVSIZ; } else { partial = length; length = 0; } /* uses an explicit flag rather than "continue" for clarity... */ toptr = tobuf; for (idx = partial; idx > 0; idx--, bptr++) { doconv = FALSE; switch (transfrom) { case -1: /* convert from current system's terminator */ DEFCONVFROM; break; case 0: if (*bptr == 0x0d) /* CR */ doconv = TRUE; break; case 1: if (*bptr == 0x0a) /* LF */ doconv = TRUE; break; case 2: if ((*bptr == 0x0a) && (lastseen == 0x0d)) { doconv = TRUE; toptr--; /*already outputed CR; back up over it*/ } lastseen = *bptr; break; } if (doconv) { switch (transto) { case -1: /* convert to current system's terminator */ DEFCONVTO; break; case 0: *(toptr++) = 0x0d; break; case 1: *(toptr++) = 0x0a; break; case 2: *(toptr++) = 0x0d; *(toptr++) = 0x0a; break; } } else { *(toptr++) = *bptr; } } /* for loop */ if (write(dstfd, tobuf, (toptr-tobuf)) != (toptr-tobuf)) Fatal("Dest write failed", procName); } /* while loop */ return (origlength); } /* * Read a file, and place in another file at current posn. We can't read more * than PAKBUFSIZ at a time, so for some files it will have to be broken down * into pieces. Note PAKBUFSIZ is expected to be an int (defined in nupak.h), * and can't be any larger than read() can handle (64K... unsigned 16-bit int). * * The transl option is present for NuUpdate and NuDelete, which have to * copy old records to a new archive w/o performing translation. */ void FCopy(srcfd, dstfd, length, copybuf, transl) int srcfd; /* source file descriptor (must be open & seek()ed) */ int dstfd; /* destination file descriptor (must be open & seek()ed) */ fourbyt length; /* number of bytes to copy */ onebyt *copybuf; BOOLEAN transl; /* maybe do text translation? */ { unsigned int partial; /* size for partial read/write */ static char *procName = "FCopy"; if (transl) lastseen = '\0'; while (length != 0L) { if (length > (long) PAKBUFSIZ) { partial = (unsigned int) PAKBUFSIZ; length -= (long) PAKBUFSIZ; if (verbose) Spin(); } else { partial = (unsigned int) length; length = 0L; } if (read(srcfd, copybuf, partial) != partial) Fatal("Source read failed", procName); if (transl) { /* do text translation if user wants it */ if (crlf(dstfd, copybuf, partial) != partial) Fatal("Dest write failed (c)", procName); } else { /* NEVER do translation */ if (write(dstfd, copybuf, partial) != partial) Fatal("Dest write failed (w)", procName); } } } /* * Add a range of bytes from one file into another, packing them. * * Set up stuff, then call the appropriate pack routine. Returns the actual * algorithm used (thread_format), since the compression algorithm could * fail, storing the file in uncompressed format instead. The packed length * is stored in a global variable. */ twobyt PackFile(srcfd, dstfd, thread_eof, thread_format, buffer) int srcfd; /* source file descriptor (must be open & seek()ed) */ int dstfd; /* destination file descriptor (must be open & seek()ed) */ fourbyt thread_eof; /* size of input */ int thread_format; /* how to pack the bytes */ onebyt *buffer; /* alloc in main prog so we don't have to each time */ { long length = (long) thread_eof; static char *procName = "PackFile"; twobyt retval = thread_format; /* default = successful pack */ switch (thread_format) { case 0x0000: /* uncompressed */ if (verbose) { printf("storing...", thread_format); fflush(stdout); } FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; break; case 0x0001: /* SQUeeze */ if (verbose) { printf("[can't squeeze; storing]..."); fflush(stdout); } else { printf("WARNING: can't squeeze; files stored uncompressed\n"); } FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; retval = 0x0000; /* uncompressed */ break; case 0x0002: /* LZW (ShrinkIt) */ if (verbose) { printf("shrinking..."); fflush(stdout); } /* packedSize set by pak_SHK */ retval = pak_SHK(srcfd, dstfd, length, buffer); break; default: fprintf(stderr, "\nUnknown compression method %d\n", thread_format); fprintf(stderr, "Aborting.\n"); Quit(-1); } return (retval); } /* * Extract a range of bytes from one file into another, unpacking them. * * Set up stuff, then call the appropriate unpack routine. Leaves the srcfd * positioned past the data to be unpacked; the calling routine should not * have to do any seeks. * * Returns TRUE if able to unpack, FALSE if not able to. Note that srcfd * WILL be seeked even if the compression method is not handled. */ int UnpackFile (srcfd, dstfd, comp_thread_eof, thread_eof, thread_format, buffer) int srcfd; /* source file descriptor (must be open & seek()ed) */ int dstfd; /* destination file descriptor (must be open & seek()ed) */ fourbyt comp_thread_eof; /* number of bytes in source */ fourbyt thread_eof; /* number of bytes to output */ int thread_format; /* how to unpack the bytes */ onebyt *buffer; { long length = (long) comp_thread_eof; static char *procName = "UnpackFile"; BOOLEAN retval = TRUE; /* default to success */ switch (thread_format) { case 0x0000: /* uncompressed */ if (verbose) { printf("extracting...", thread_format); fflush(stdout);} FCopy(srcfd, dstfd, length, buffer, TRUE); break; case 0x0001: /* unSQUeeze */ #ifdef NO_BLU if (verbose) { printf("[can't unsqueeze - aborting]..."); fflush(stdout); } else { printf("ERROR: can't unsqueeze; 'squ' files not extracted\n"); } lseek(srcfd, length, S_REL); /* set file posn */ retval = FALSE; #else if (verbose) { printf("unsqueezing..."); fflush(stdout); } unpak_SQU(srcfd, dstfd, length); /* thread_eof not needed */ #endif break; case 0x0002: /* LZW (ShrinkIt) */ if (verbose) { printf("unshrinking..."); fflush(stdout); } unpak_SHK(srcfd, dstfd, comp_thread_eof, thread_eof, buffer); break; default: fprintf(stderr, "Unknown uncompression method %d\n", thread_format); lseek(srcfd, length, S_REL); /* set file posn */ retval = FALSE; break; } return (retval); } !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive