Path: ns-mx!uunet!sun-barr!newstop!sun!amdahl!fadden From: fadden@uts.amdahl.com (Andy McFadden) Newsgroups: comp.binaries.apple2 Subject: NuLib v3.10 (UNIX) nulib.05 Message-ID: <5aKF02OS094500@amdahl.uts.amdahl.com> Date: 1 Nov 91 04:12:27 GMT Reply-To: fadden@amdahl.uts.amdahl.com (Andy McFadden) Organization: Amdahl Corporation, Sunnyvale CA Lines: 1449 NuLib v3.10 - nulib.05 ---- Cut Here and feed the following to sh ---- #!/bin/sh # This is part 05 of a multipart archive # ============= nuetc.c ============== if test -f 'nuetc.c' -a X"$1" != X"-c"; then echo 'x - skipping nuetc.c (File already exists)' else echo 'x - extracting nuetc.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'nuetc.c' && /* X * nuetc.c - extra stuff; mostly system-dependent subroutines. X * X * NuLib v3.1 October 1991 Freeware (distribute, don't sell) X * By Andy McFadden (fadden@cory.berkeley.edu) X */ #ifdef APW segment "NuMain" #endif X #include "nudefs.h" #include #include #include /* errno declarations */ #include /* for tolower(), isupper() */ X #ifdef UNIX # include /* need localtime() */ # include /* defn of time_t */ # include #endif #ifdef APW # include /* exit(), etc. */ # include /* has _toolErr in it */ # include # include "apwerr.h" /* APW/ProDOS error codes */ #endif #ifdef MSDOS # include # include # include # include # include # include #endif X #include "nuetc.h" /* note "nuread.h" is not included... none of the routines here assume */ /* any knowledge of NuFX archives. */ X #ifndef MSDOS extern char *malloc(); /* all systems ... except DOS : RBH */ #endif /* +PORT+ */ X extern void free(); extern char *getenv(); #ifdef APW extern Time ReadTimeHex(); /* should be TimeRec, from misctool.h */ #endif X /* This is a generally available TEMPORARY filename buffer */ char tmpNameBuf[MAXFILENAME]; X X /******************** general misc routines ********************/ X /* X * Fatal error handler X */ void Fatal(deathstr, procName) char *deathstr, *procName; { X fflush(stdout); X fprintf(stderr, "\n%s: fatal error: %s\n--- ", prgName, deathstr); X perror(procName); X Quit (-1); } X X /* X * Quit can be used to perform cleanup operations before exiting. X */ void Quit(val) int val; { X exit(val); } X X /* X * Safe malloc()... checks return value X */ char * Malloc(size) int size; { X char *ptr = (char *) malloc(size); X X if (ptr != (char *) NULL) { X return(ptr); X } else { X /* 8910.31 - RBH: report byte size that failed */ X printf("Malloc: memory alloc error [%u : bytes]\n", size); #ifdef MSDOS X printf("(Largest Available Block: %u)\n", _memmax()); #endif X Quit (-1); X } } X /******************** UNIX compatibility routines ********************/ X #ifdef UNIX /* X * Convert expanded date to a number of seconds for UNIX systems. X * X * Remember that *atime follows the NuFX docs for time values, which X * don't necessarily match those in UNIX manual pages. X * Adapted from _Advanced UNIX Programming_ by Marc J. Rochkind. X * X * Returns 0 if date/time is invalid. X */ long timecvt(atime) Time *atime; { X long tm; X int days; X BOOLEAN isleapyear; X int tzone; X char *getenv(), *tz; X X if ((tz = getenv("TZ")) == NULL) X tzone = 8; /* pacific std time */ X else X tzone = atoi(&tz[3]); X X isleapyear = (atime->year != 0) && (atime->year%4 == 0); /* 2000 isn't */ X if (atime->year < 70) X atime->year += 100; /* years after 2000 */ X days = (atime->year - 70) * 365L; X days += ((atime->year - 69L) / 4); /* previous years' leap days */ X X switch (atime->month +1) { /* month is 0-11 */ X case 12: X days += 30; /* Nov */ X case 11: X days += 31; /* Oct */ X case 10: X days += 30; /* Sep */ X case 9: X days += 31; /* Aug */ X case 8: X days += 31; /* Jul */ X case 7: X days += 30; /* Jun */ X case 6: X days += 31; /* May */ X case 5: X days += 30; /* Apr */ X case 4: X days += 31; /* Mar */ X case 3: X days += (isleapyear ? 29 : 28); /* Feb */ X case 2: X days += 31; /* Jan */ X case 1: X break; X default: X /*printf("Invalid month\n");*/ X return (0L); X } X X if (atime->day > 31) { X /*printf("Invalid day\n");*/ X return (0L); X } X tm = (days + atime->day) * 24L * 60L * 60L; X X if (atime->hour > 23) { X /*printf("Invalid hour\n");*/ X return (0L); X } X atime->hour += tzone; /* correct for time zone */ X tm += atime->hour * 60L * 60L; X X if (atime->minute > 59) { X /*printf("Invalid minute\n");*/ X return (0L); X } X tm += atime->minute * 60L; X X if (atime->second > 59) { X /*printf("Invalid second\n");*/ X return (0L); X } X tm += atime->second; X X if (localtime(&tm)->tm_isdst) /* was that day in dst? */ X tm -= 60L * 60L; /* adjust for daylight savings */ X return (tm); } #endif /* UNIX */ X /******************** APW compatibility routines ********************/ X #ifdef APW /* X * Normally a C library function to print out a description of the most X * recent system (non-toolbox, non-ProDOS) error. Exists under UNIX and X * MS C 5.1, so I'm assuming it exists most everywhere else... X */ void perror(errstr) char *errstr; { X fflush(stdout); X if ( (errno > 0) && (errno < sys_nerr) ) { /* known APW error? */ X fprintf(stderr, "%s: %s\n", errstr, sys_errlist[errno]); X } else { X fprintf(stderr, "%s: ", errstr); X fflush(stderr); X ERROR( errno ); X } X Quit (-1); } X X /* Check for //gs toolbox errors; all are fatal */ void ToolErrChk() { X int err = _toolErr; X X if (err) { X if (err < MPErr) { /* was a ProDOS error? */ X fprintf(stderr, "Error: $%.2x %s\n", (char) err, X ProDOSErr[err]); X } else { X fprintf(stderr, "Tool err ($%.4x): ", err); X fflush(stderr); X ERROR( err ); X } X Quit (-1); X } } X #endif /* APW */ X /******************** miscellaneous string routines ********************/ X /* X * Compare strings, ignoring case (may be in standard C lib; stricmp()?) X */ int strcasecmp(str1, str2) register char *str1, *str2; { X register char one, two; X register int val; X X for ( ; *str1 && *str2; str1++, str2++) { X one = (isupper(*str1) ? tolower(*str1) : *str1); X two = (isupper(*str2) ? tolower(*str2) : *str2); X if (val = two - one) X return (val); X } X if (!(*str1) && !(*str2)) /* both zero -> equivalent */ X return (0); X else { /* one is shorter; return result */ X one = (isupper(*str1) ? tolower(*str1) : *str1); X two = (isupper(*str2) ? tolower(*str2) : *str2); X return (two - one); X } } X int strncasecmp(str1, str2, num) register char *str1, *str2; int num; { X register int i; X register char one, two; X register int val; /* keep going 'til no more registers... */ X X for (i = 0; (i < num) && (*str1) && (*str2); i++, str1++, str2++) { X one = (isupper(*str1) ? tolower(*str1) : *str1); X two = (isupper(*str2) ? tolower(*str2) : *str2); X if (val = two - one) X return (val); X } X if (i == num) /* first num characters are equal, so return zero */ X return (0); X else { /* one ended early; return result */ X one = (isupper(*str1) ? tolower(*str1) : *str1); X two = (isupper(*str2) ? tolower(*str2) : *str2); X return (two - one); X } } X /******************* file-related routines ********************/ X /* X * Do operating system-dependent CREATE stuff X * X * Creates a NuFX archive file, with type info where necessary. X * Does not leave file open. X */ void ArcfiCreate(filename) char *filename; { X static char *procName = "ArcfiCreate"; #ifdef UNIX X int fd; X X if ((fd = open(filename, O_CREAT|O_RDWR, WPERMS)) < 0) X Fatal("Unable to create file", procName); X close(fd); #else # ifdef APW X FileRec create_p; X X c2pstr(filename); X create_p.pathname = filename; X create_p.fAccess = 0x00e3; X create_p.fileType = 0x00e0; /* LBR */ X create_p.auxType = 0x8002; /* SHK */ X create_p.storageType = 0x0001; X create_p.createDate = 0x0000; /* let ProDOS fill in the blanks */ X create_p.createTime = 0x0000; X CREATE( &create_p ); X ToolErrChk(); X p2cstr(filename); # endif /* APW */ # ifdef MSDOS X int fd; X X if ((fd = open(filename, O_CREAT|O_RDWR, WPERMS)) < 0) X Fatal("Unable to create file", procName); X close(fd); # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X int fd; X X if ((fd = open(filename, O_CREAT|O_RDWR, WPERMS)) < 0) X Fatal("Unable to create file", procName); X close(fd); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } X X /* X * Determine if a file already exists. X */ BOOLEAN Exists(filename) char *filename; { X static char *procName = "Exists"; #ifdef UNIX X struct stat sm; X X if (stat(filename, &sm) < 0) { X if (errno == ENOENT) /* if doesn't exist, then okay */ X return (FALSE); X else /* some other problem killed stat(), probably serious */ X fprintf(stderr, "Unable to stat() '%s'\n", filename); X Fatal("Bad stat()", procName); /*serious prob*/ X } else /* successful call - file exists */ X return (TRUE); #else # ifdef APW X FileRec info_p; /* check if file exists, is dir */ X int err; X X c2pstr(filename); X info_p.pathname = filename; X GET_FILE_INFO( &info_p ); X err = _toolErr; X p2cstr(filename); X if (err == pathNotFound || err == fileNotFound) X return (FALSE); X else if (!err) X return (TRUE); X else { X _toolErr = err; X ToolErrChk(); X return (TRUE); X } # endif /* APW */ # ifdef MSDOS X struct stat sm; X X if (stat(filename, &sm) < 0) { X if (errno == ENOENT) /* if doesn't exist, then okay */ X return (FALSE); X else /* some other problem killed stat(), probably serious */ X fprintf(stderr, "Unable to stat() '%s'\n", filename); X Fatal("Bad stat()", procName); /*serious prob*/ X } else /* successful call - file exists */ X return (TRUE); # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X printf("Need [other] Exists()\n"); /* +PORT+ */ X return (FALSE); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } X X /* X * Generate a temporary file name (system dependent). X * Assumes space is allocated for buffer. X */ char * MakeTemp(buffer) char *buffer; { X static char *procName = "MakeTemp"; #ifdef UNIX X extern char *mktemp(); X X strcpy(buffer, "nulb.tmpXXXXXX"); X return (mktemp(buffer)); #else # ifdef APW X int idx = 0; X X do { X sprintf(buffer, "nulb.tmp%d", idx++); X } while (Exists(buffer)); X return (buffer); # endif /* APW */ # ifdef MSDOS X extern char *mktemp(); X X strcpy(buffer, "nulbXXXX.tmp"); X return (mktemp(buffer)); # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X strcpy(buffer, "nulb.tmp"); /* +PORT+ */ X return (buffer); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } X #ifdef NO_RENAME /* X * This is a replacement for the library call, in case somebody's C library X * doesn't have it. X */ int rename(fromname, toname) { X if (link(fromname, toname) < 0) X return (-1); X if (unlink(fromname) < 0) X return (-1); } #endif X X /* X * Rename a file. X */ void Rename(fromname, toname) char *fromname, *toname; { X static char *procName = "Rename"; #ifdef UNIX X if (Exists(toname)) { X fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", X prgName, fromname, toname); X fflush(stderr); X } # ifdef AOSVS /* BAK 04/30/90 */ X printf("Work on AOS/VS rename command\n"); /* BAK 04/30/90 */ # else /* BAK 04/30/90 */ X else { X if (rename(fromname, toname) < 0) { /* this should "never" fail */ X fprintf(stderr, X "\n%s: WARNING: Unable to rename '%s' as '%s'\n", X prgName, fromname, toname); X Fatal("Bad rename()", procName); /*serious prob*/ X } X } # endif #else # ifdef APW X PathNameRec cpath_p; X X if (Exists(toname)) { X fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", X prgName, fromname, toname); X fflush(stderr); X return; X } X X cpath_p.pathname = fromname; X cpath_p.newPathname = toname; X c2pstr(fromname); X c2pstr(toname); X CHANGE_PATH( &cpath_p ); X ToolErrChk(); X p2cstr(fromname); X p2cstr(toname); # endif /* APW */ # ifdef MSDOS X if (Exists(toname)) { X fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", X prgName, fromname, toname); X fflush(stderr); X return; X } X printf("Work on MSDOS rename command\n"); # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X if (Exists(toname)) { X fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", X prgName, fromname, toname); X fflush(stderr); X return; X } X printf("Need [other] rename command\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /*UNIX*/ } X /******************** date/time routines ********************/ X /* X * Expand date/time from file-sys dependent format to eight byte NuFX format. X * tptr is filesys format, TimePtr is NuFX format X */ void ExpandTime(tptr, TimePtr) /* (BSD) UNIX version */ onebyt *tptr; /* usually points to a time_t (long) */ Time *TimePtr; { #ifdef UNIX X time_t *tp = (time_t *) tptr; X struct tm *unixt; X X unixt = localtime(tp); /* expand time_t into components */ X TimePtr->second = unixt->tm_sec; X TimePtr->minute = unixt->tm_min; X TimePtr->hour = unixt->tm_hour; X TimePtr->year = unixt->tm_year; X TimePtr->day = unixt->tm_mday -1; /* want 0-xx, not 1-xx */ X TimePtr->month = unixt->tm_mon; X TimePtr->extra = 0; X TimePtr->weekDay = unixt->tm_wday +1; /* Sunday = 1, not 0 like UNIX */ #else # ifdef APW /* APW version */ X twobyt date, time; X X date = (twobyt)tptr[0] + ((twobyt)tptr[1] << 8); X time = (twobyt)tptr[2] + ((twobyt)tptr[3] << 8); X TimePtr->second = 0; /* not stored in ProDOS file info */ X TimePtr->minute = (char) time; /* truncated to char */ X TimePtr->hour = time >> 8; X TimePtr->year = date >> 9; X TimePtr->day = (date & 0x1f) - 1; X TimePtr->month = ((date & 0x01e0) >> 5) - 1; X TimePtr->extra = 0; X TimePtr->weekDay = 0; # endif /* APW */ # ifdef MSDOS X struct tm *newtime; X time_t *tp = (time_t *) tptr; X X newtime = localtime (tp); X TimePtr->second = (onebyt)newtime->tm_sec; X TimePtr->minute = (onebyt)newtime->tm_min; X TimePtr->hour = (onebyt)newtime->tm_hour; X TimePtr->year = (onebyt)newtime->tm_year; X TimePtr->day = (onebyt)newtime->tm_mday - 1; X TimePtr->month = (onebyt)newtime->tm_mon; X TimePtr->extra = 0; X TimePtr->weekDay= (onebyt)newtime->tm_wday + 1; # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X printf("Need [other] time-expander\n"); /* +PORT+ */ X TimePtr->second = 0; X TimePtr->minute = 0; X TimePtr->hour = 0; X TimePtr->year = 0; X TimePtr->day = 0; X TimePtr->month = 0; X TimePtr->extra = 0; X TimePtr->weekDay = 0; # endif /* none1 */ # endif /* none2 */ #endif /* UNIX */ } X X /* X * Get current time, put in struct X */ Time * GetTime() { X static Time t; #ifdef UNIX X struct tm *unixt; X time_t now = time(NULL); X X unixt = localtime(&now); X t.second = unixt->tm_sec; X t.minute = unixt->tm_min; X t.hour = unixt->tm_hour; X t.year = unixt->tm_year; X t.day = unixt->tm_mday -1; /* want 0-xx, not 1-xx */ X t.month = unixt->tm_mon; X t.extra = 0; X t.weekDay = unixt->tm_wday +1; /* Sunday = 1, not 0 like UNIX */ X /* return (&t) */ #else # ifdef APW X t = ReadTimeHex(t); X /* return (&t) */ # endif /* APW */ # ifdef MSDOS X struct tm *pctime; X time_t now = time(NULL); X X pctime = localtime(&now); X t.second = (onebyt)pctime->tm_sec; X t.minute = (onebyt)pctime->tm_min; X t.hour = (onebyt)pctime->tm_hour; X t.year = (onebyt)pctime->tm_year; X t.day = (onebyt)pctime->tm_mday -1; /* want 0-xx, not 1-xx */ X t.month = (onebyt)pctime->tm_mon; X t.extra = 0; X t.weekDay= (onebyt)pctime->tm_wday +1; /* Sunday = 1, not 0 */ X /* return (&t) */ # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X printf("\nNeed [other] GetTime\n"); /* +PORT+ */ X t->second = 0; X t->minute = 0; X t->hour = 0; X t->year = 0; X t->day = 0; X t->month = 0; X t->filler = 0; X t->weekDay = 0; X /* return (&t) */ # endif /* none1 */ # endif /* none2 */ #endif /* UNIX */ X return (&t); } X X /* X * Convert a NuFX Time struct to a compact system-dependent format X * X * This is used to set a file's date when extracting. Most systems don't X * dedicate 8 bytes to storing the date; this reduces it to the format X * used by a "set_file_date" command. X */ long ReduceTime(tptr) Time *tptr; { #ifdef UNIX X long t = timecvt(tptr); X X return (t ? t : time(NULL)); /* if stored time is invalid, */ X /* return current time */ #else # ifdef APW X twobyt date, time; X long val; X X date = ((twobyt)tptr->year << 9) | ((((twobyt)tptr->month)+1) << 5) | X (((twobyt)tptr->day)+1); X time = ((twobyt)tptr->hour << 8) | ((twobyt)tptr->minute); X X val = (long) date + ((long) time << 16); X return (val); # endif /* APW */ # ifdef MSDOS X return (time(NULL)); /* not sure what to do, return current : RBH */ # endif /* MSDOS */ X #ifndef APW #ifndef MSDOS X printf("Need [other] ReduceTime\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } X SHAR_EOF chmod 0644 nuetc.c || echo 'restore of nuetc.c failed' Wc_c="`wc -c < 'nuetc.c'`" test 16532 -eq "$Wc_c" || echo 'nuetc.c: original size 16532, current size' "$Wc_c" fi # ============= nuext.c ============== if test -f 'nuext.c' -a X"$1" != X"-c"; then echo 'x - skipping nuext.c (File already exists)' else echo 'x - extracting nuext.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'nuext.c' && /* X * nuext.c - operations which extract from a NuFX archive X * X * NuLib v3.1 October 1991 Freeware (distribute, don't sell) X * By Andy McFadden (fadden@cory.berkeley.edu) X */ #ifdef APW segment "NuMain" #endif X #include "nudefs.h" #include #ifdef BSD43 # include #else /* SYSV, APW, MSC */ # include #endif #include X #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 X #include "nuread.h" #include "nuext.h" #include "nupak.h" #include "nuetc.h" X static BOOLEAN extall; /* extract all files? */ static BOOLEAN print; /* extract to screen rather than file? */ X X /* X * Get the answer to a yes/no question. X * X * Returns TRUE for yes, FALSE for no. May return additional things in the X * future... (y/n/q)? X */ int AskYesNo() { X char buf[16]; /* if user answers with >16 chars, bad things happen */ X char c; X X printf(" (y/n)? "); X fflush(stdout); X gets(buf); X c = *buf; X if ((c == 'y') || (c == 'Y')) X return (TRUE); X else X return (FALSE); } X X /* X * Convert a filename to one legal in the present file system. X * X * Does not allocate new space; alters string in place (so original string X * will be "corrupted"). Assumes that it has been passed a filename without X * the filename separators. X */ void ConvFileName(str) char *str; { X int idx = 0; #ifdef UNIX X X while (*str != '\0') { X if ((*str > 127) || (*str < 0)) *str &= 0x7f; /* clear hi bit */ X if (*str == '/') *str = '.'; X if (++idx > 255) { *str = '\0'; break; } /* MAXNAMELEN? */ X str++; X } #else # ifdef APW X static char *legal = X "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789."; X X /* assumes ProDOS limits, not GS/OS */ X if ( ((*str < 'A') && (*str > 'Z')) || ((*str < 'a') && (*str > 'z')) ) X *str = 'X'; /* must start with alpha char */ X while (*str != '\0') { X if (!INDEX(legal, *str)) *str = '.'; X if (++idx > 15) { *str = '\0'; break; } X str++; X } # endif /* APW */ # ifdef MSDOS X while (*str != '\0') { X if ((*str > 127) || (*str < 0)) *str &= 0x7f; /* clear hi bit */ X if (*str == '/') *str = '_'; X if (*str == '\\') *str = '_'; X if (*str == '!') *str = '_'; X if (*str == ':') *str = '_'; X if (++idx > 255) { *str = '\0'; break; } X str++; X } # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X printf("Need [other] filename converter\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /*UNIX*/ } X /* X * Set a file's attributes according to info in a record structure. X */ void SetFInfo(filename, RHptr) char *filename; RHblock *RHptr; { X static char *procName = "SetFInfo"; #ifdef UNIX X long ltime; X time_t timep[2]; X X ltime = ReduceTime(&RHptr->mod_when); /* set both to mod time */ X timep[0] = ltime; /* accessed */ X timep[1] = ltime; /* modified */ X utime(filename, timep); X X if ((RHptr->access == 0xe3L) || (RHptr->access == 0xc3L)) /* unlocked */ X chmod(filename, S_IREAD | S_IWRITE | 044); X if ((RHptr->access == 0x21L) || (RHptr->access == 0x01L)) /* locked */ X chmod(filename, S_IREAD | 044); X #else /* UNIX */ # ifdef APW X /* X * Call ProDOS SET_FILE_INFO to set attributes for a file. X * Uses the information in the record header block. X */ X FileRec finfo; X OpenRec oinfo; X twobyt date, time; X long ltime; X X finfo.pathname = c2pstr(filename); /* temp storage...? */ X finfo.fAccess = (twobyt) RHptr->access; X finfo.fileType = (twobyt) RHptr->file_type; X finfo.auxType = RHptr->extra_type; X finfo.storageType = 0; /* RHptr->storage_type otherwise */ X ltime = ReduceTime(&RHptr->create_when); X date = (twobyt) ltime; /* date is lower 16 */ X time = (twobyt) (ltime >> 16); /* time is upper */ X finfo.createDate = date; X finfo.createTime = time; X ltime = ReduceTime(&RHptr->mod_when); X date = (twobyt) ltime; /* date is lower 16 */ X time = (twobyt) (ltime >> 16); /* time is upper */ X finfo.modDate = date; X finfo.modTime = time; X X SET_FILE_INFO( &finfo ); X ToolErrChk(); # endif /* APW */ # ifdef MSDOS X long ltime; X time_t timep[2]; X X ltime = ReduceTime(&RHptr->mod_when); X timep[0] = ltime; /* accessed */ X timep[1] = ltime; /* modified */ X utime(filename, timep); X X if ((RHptr->access == 0xe3L) || (RHptr->access == 0xc3L)) /* unlocked */ X chmod(filename, S_IREAD | S_IWRITE | 044); X if ((RHptr->access == 0x21L) || (RHptr->access == 0x01L)) /* locked */ X chmod(filename, S_IREAD | 044); # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X printf("need [other] SetFInfo stuff\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /* APW */ } X X /* X * Create a subdirectory X * X * This routine will exit on most errors, since generally more than one file X * will be unpacked to a given subdirectory, and we don't want it charging X * bravely onward if it's going to run into the same problem every time. X */ void CreateSubdir(pathname) char *pathname; { X char *buffer = (char *) Malloc(MAXFILENAME+6); X static char *procName = "CreateSubdir"; #ifdef UNIX X struct stat st; X X /* if no directory exists, then make one */ X if (stat(pathname, &st) < 0) X if (errno == ENOENT) { X sprintf(buffer, "mkdir %s", pathname); X if (system(buffer) != 0) /* call UNIX mkdir to create subdir */ X Fatal("Unable to create subdir", procName); X } else { X Fatal("Unable to create dir", procName); X } #else # ifdef APW X static FileRec create_p = { "", 0x00e3, 0x000f, 0L, 0x000d, 0, 0 }; /*dir*/ X FileRec info_p; /* check if file exists, is dir */ X int err; /* holds _toolErr */ X X strcpy(buffer, pathname); X c2pstr(buffer); X info_p.pathname = buffer; X GET_FILE_INFO( &info_p ); X X switch (_toolErr) { X case 0x0000: /* no error */ X if (info_p.storageType != 0x000d) /* not a DIR? */ X Fatal("File in path exists, is not a directory.", procName); X return; /* file exists, is directory, no need to create */ X X case fileNotFound: X create_p.pathname = buffer; X CREATE( &create_p ); X if (!_toolErr) return; /* created okay? */ X else ToolErrChk(); X X default: /* unknown error */ X ToolErrChk(); X Fatal("whoops!", procName); /* shouldn't get here */ X } # endif /* APW */ # ifdef MSDOS X struct stat st; X X /* if no directory exists, then make one */ X if (stat(pathname, &st) < 0) X if (errno == ENOENT) { X if (mkdir(pathname) != 0) X Fatal("Unable to create subdir", procName); X } else { X Fatal("Unable to create dir", procName); X } # endif /* MSDOS */ X # ifndef APW # ifndef MSDOS X X /* don't forget to check if it exists first... */ /* +PORT+ */ X printf("don't know how to create [other] subdirectories\n"); /* mkdir() */ # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ X free(buffer); } X X /* X * Given a pathname, create subdirectories as needed. All file names are run X * through a system-dependent filename filter, which means that the pathname X * has to be broken down, the subdirectory created, and then the pathname X * reconstructed with the "legal" pathname. The converted filename is held X * in a static buffer; subsequent calls will overwrite the previous string. X * X * This is useful when unpacking "dir1/dir2/fubar" and dir1 and dir2 don't X * necessarily exist. X * X * It is assumed that all filenames are relative to the current directory. X * According to the NuFX docs (revision 3 2/3/89), initial separators (like X * "/", "\", or ":") should NOT be included. If they are, this routine may X * break. X */ static char * CreatePath(pathname, fssep) char *pathname; /* full pathname; should not include ProDOS volume name */ onebyt fssep; /* file system pathname separator, usually "/" or "\" */ { X int idx; X char *ptr; X static char workbuf[MAXFILENAME]; /* work buffer; must be static */ X static char *procName = "CreatePath"; X X idx = 0; X while (TRUE) { /* move through string */ X ptr = INDEX(pathname, fssep); /* find break */ X if (ptr) /* down to actual filename? */ X *ptr = '\0'; /* no, isolate this part of the string */ X X strcpy(&workbuf[idx], pathname); /* copy component to buf */ X ConvFileName(&workbuf[idx]); /* convert to legal str; may be shorter */ X idx += strlen(&workbuf[idx]); /* advance index to end of string */ X if (!ptr) { /* down to actual filename? */ X workbuf[idx] = '\0'; /* yes, clean up */ X break; /* out of while */ X } X workbuf[idx] = '\0'; X X CreateSubdir(workbuf); /* system-dependent dir create */ X #ifdef UNIX X workbuf[idx++] = '/'; /* tack a filename separator on, and advance */ X *ptr = '/'; /* be nice */ #else # ifdef APW X workbuf[idx++] = '/'; X *ptr = '/'; # endif # ifdef MSDOS X workbuf[idx++] = '\'; X *ptr = '\'; # endif # ifndef APW /* +PORT+ */ # ifndef MSDOS X workbuf[idx++] = '/'; X *ptr = '/'; # endif # endif #endif /*UNIX*/ X /* was: workbuf[idx++] = fssep; /* tack an fssep on the end, and advance */ /* was: *ptr = fssep; /* be nice */ X X pathname = ptr+1; /* go again with next component */ X } X X return (workbuf); } X X /* X * Extract a thread, and place in a file. X * X * Returns TRUE if the extract was successful, FALSE otherwise. The most X * common reason for a FALSE return value is a "no" answer when asked about X * overwriting an existing file. X */ 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 */ { X int dstfd; /* destination file descriptor */ X static char *procName = "ExtractThread"; X X if (!print) { X if (Exists(destpn)) { X if (interact) { X if (verbose) printf("file exists, overwite"); X else printf("%s exists, overwite", destpn); X if (!AskYesNo()) { /* return w/o overwriting */ X return (FALSE); X } X } X if (verbose) { printf("overwriting..."); fflush(stdout); } X if (unlink(destpn) < 0) X Fatal("Unable to remove existing file", procName); X } X X if ((dstfd = X open(destpn, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, WPERMS)) < 0) X Fatal("Unable to open target path", procName); X X if (lseek(arcfd, fileposn, S_ABS) < 0) X Fatal("Seek failed", procName); X X if (!UnpackFile(arcfd, dstfd, THptr, X dopack ? THptr->thread_format : 0, pakbuf)) { X if (close(dstfd) < 0) X Fatal("Dest close failed", procName); X unlink(destpn); /* some sys can't delete while file open */ X } else { X if (close(dstfd) < 0) X Fatal("Dest close failed", procName); X } X X } else { /* print */ X if ((dstfd = fileno(stdout)) < 0) X Fatal("Unable to get file for stdout", procName); X if (lseek(arcfd, fileposn, S_ABS) < 0) X Fatal("Seek failed", procName); X X if (!UnpackFile(arcfd, dstfd, THptr, X dopack ? THptr->thread_format : 0, pakbuf)) { X printf("Unpack failed.\n"); X return (FALSE); X } X fflush(stdout); X } X X return (TRUE); } X X /* X * Handle message_threads X */ static void message_thread(arcfd, RNodePtr, TNodePtr) int arcfd; RNode *RNodePtr; TNode *TNodePtr; { X int i; X int oldTo, oldFrom; X static char *procName = "message_thread"; X X switch (TNodePtr->THptr->thread_kind) { X case 0x0000: /* ASCII text */ X printf("Found obsolete ASCII text thread (ignored)\n"); X break; X case 0x0001: /* ASCII text, predefined size */ X if (verbose && !print && TNodePtr->THptr->thread_eof) { X printf("\n--- Comment for file '%s':\n", RNodePtr->filename); X fflush(stdout); X if (lseek(arcfd, TNodePtr->fileposn, S_ABS) < 0) X Fatal("unable to seek to comment", procName); X oldTo = transto; X oldFrom = transfrom; X transto = -1; /* switch to CR -> current mode */ X transfrom = 0; /* (assumes created under ProDOS) */ X /* may need to fix this later (but how?) */ X FCopy(arcfd, fileno(stdout), TNodePtr->THptr->thread_eof, X pakbuf, TRUE); #ifdef FUBAR X print = TRUE; X verbose = FALSE; /* turn off "unshrinking..." messages */ X ExtractThread(arcfd,TNodePtr->fileposn, "stdout", TNodePtr->THptr); X print = FALSE; X verbose = TRUE; #endif X transto = oldTo; X transfrom = oldFrom; X putchar('\n'); X } X break; X case 0x0002: /* standard Apple IIgs icon */ X printf("Found Apple IIgs Icon thread (ignored)\n"); X break; X default: X printf("Found unknown message_thread %.4x in '%s'\n", X TNodePtr->THptr->thread_kind, RNodePtr->filename); X break; X } } X /* X * Handle control_threads X */ static void control_thread(arcfd, RNodePtr, TNodePtr) int arcfd; RNode *RNodePtr; TNode *TNodePtr; { X switch (TNodePtr->THptr->thread_kind) { X case 0x000: /* create dir */ X printf("Found create directory control thread (ignored)\n"); X break; X default: X printf("Found unknown control_thread %.4x in '%s'\n", X TNodePtr->THptr->thread_kind, RNodePtr->filename); X break; X } } X X /* X * Handle data_threads X * X * Does not guarantee that the archive file position is anything rational; X * the TNode's fileposn should be (and is) used here. X */ static void data_thread(arcfd, RNodePtr, TNodePtr) int arcfd; RNode *RNodePtr; TNode *TNodePtr; { X long fileposn; /* absolute position of thread in file */ X long old_eof; X char *fn; X int ov; X X if (print) /* this is something of a hack... */ X if (TNodePtr->THptr->thread_kind != 0x0000) { /* not a data fork? */ X fprintf(stderr, "Can't print non-data fork for '%s'.\n", X RNodePtr->filename); X return; /* this hoses the file posn... */ X } else { X if (verbose) printf("\n***** %s *****\n", RNodePtr->filename); X fflush(stdout); X ov = verbose; X verbose = FALSE; /* turn off "unshrinking..." messages */ X fileposn = TNodePtr->fileposn; X ExtractThread(arcfd,fileposn, "stdout", TNodePtr->THptr); X verbose = ov; X return; X } X X switch (TNodePtr->THptr->thread_kind) { X case 0x0000: /* data fork */ X if (verbose) { X printf("Extracting '%s' (data)...", RNodePtr->filename); X fflush(stdout); X } X X /* create any needed subdirs */ X fn = CreatePath(RNodePtr->filename, RNodePtr->RHptr->file_sys_info); X X /* extract the file */ X if (ExtractThread(arcfd, TNodePtr->fileposn, fn, TNodePtr->THptr)) { X SetFInfo(fn, RNodePtr->RHptr); /* set file attributes, dates... */ X if (verbose) printf("done.\n"); X } X break; X X case 0x0001: /* disk image */ /* printf("Found disk image (not extracted)\n");*/ X X if (verbose) { X printf("Extracting '%s' (disk image)...", RNodePtr->filename); X fflush(stdout); X } X X /* setup path (normalize file name) */ X fn = CreatePath(RNodePtr->filename, RNodePtr->RHptr->file_sys_info); X X /* thread_eof is invalid for disks, so figure it out */ X old_eof = TNodePtr->THptr->thread_eof; X if (RNodePtr->RHptr->storage_type <= 3) { /* should be block */ X TNodePtr->THptr->thread_eof = /* size, but shk301 */ X RNodePtr->RHptr->extra_type * 512; /* stored it wrong */ X } else { X TNodePtr->THptr->thread_eof = X RNodePtr->RHptr->extra_type * RNodePtr->RHptr->storage_type; X } X X /* extract the disk into a file */ X if (ExtractThread(arcfd, TNodePtr->fileposn, fn, TNodePtr->THptr)) { X /*SetFInfo(fn, RNodePtr->RHptr);/* set file attributes, dates... */ X if (verbose) printf("done.\n"); X } X X TNodePtr->THptr->thread_eof = old_eof; X break; X X case 0x0002: /* resource_fork */ X printf("Found resource_fork (not extracted)\n"); X break; X default: X printf("Found unknown data_thread %.4x in '%s'\n", X TNodePtr->THptr->thread_kind, RNodePtr->filename); X break; X } } X X /* X * Extract files from archive X * X * Scan archive, extracting files which start with the strings in "names". X * Calls subroutines to handle the various thread_class types. X */ static void Extract(filename, namecount, names) char *filename; int namecount; char **names; { X ListHdr *archive; X int arcfd; /* archive file descriptor */ X int rec, idx; X MHblock *MHptr; /* Master Header block */ X RNode *RNodePtr; /* Record Node */ X TNode *TNodePtr; /* Thread block */ X int len, *lentab; /* hold strlen() of all names */ X char *pn; /* archived pathname */ X int thread; /* current thread #; max 65535 threads */ X BOOLEAN gotone = FALSE; X static char *procName = "Extract"; X X archive = NuRead(filename); X if ((arcfd = open(archive->arc_name, O_RDONLY | O_BINARY)) < 0) X Fatal("Unable to open archive", procName); X X pakbuf = (onebyt *) Malloc(PAKBUFSIZ); /* allocate unpack buffer */ X X if (!namecount) { X extall = TRUE; X lentab = (int *) NULL; X } else { X lentab = (int *) Malloc(sizeof(int) * namecount); X for (idx = 0; idx < namecount; idx++) /* calc. once (for efficiency) */ X lentab[idx] = strlen(names[idx]); X } X X MHptr = archive->MHptr; X RNodePtr = archive->RNodePtr; X X /* main record read loop */ X for (rec = 0; rec < MHptr->total_records; rec++) { X pn = RNodePtr->filename; X len = strlen(pn); X if (RNodePtr->RHptr->version_number > MAXVERS) { X printf("Unable to extract '%s': unknown record version_number\n", X pn); X continue; /* with for */ X } X X for (idx = 0; extall || idx < namecount; idx++) { /* find arced file */ X /* try to match argument with first few chars of stored filename */ X /* or the entire filename, depending on EXPAND flag */ X if (extall || ((len >= lentab[idx]) && doExpand ? X (!strncasecmp(pn, names[idx], lentab[idx])) : X (!strcasecmp(pn, names[idx])) )) { X X gotone = TRUE; X /* go through all threads */ X TNodePtr = RNodePtr->TNodePtr; X for (thread = 0; thread < (int) RNodePtr->RHptr->total_threads; X thread++) { X switch(TNodePtr->THptr->thread_class) { X case 0x0000: X message_thread(arcfd, RNodePtr, TNodePtr); X break; X case 0x0001: X control_thread(arcfd, RNodePtr, TNodePtr); X break; X case 0x0002: X /* don't extract if doMessages is set */ X if (!doMessages) X data_thread(arcfd, RNodePtr, TNodePtr); X break; X case 0x0003: X /* filename_thread; ignore */ X break; X default: X printf("Unknown thread_class %.4x for '%s'\n", X TNodePtr->THptr->thread_class, RNodePtr->filename); X break; X } X TNodePtr = TNodePtr->TNext; X } X break; /* out of filename matching (inner) FOR loop */ X } X } X X RNodePtr = RNodePtr->RNext; /* move on to next record */ X } X if (!gotone && verbose) X printf("None selected\n"); X if (close(arcfd) < 0) X Fatal("Source (archive) close failed", procName); X } X /* X * Entry point to extract routines. X */ void NuExtract(filename, namecount, names, options) char *filename; int namecount; char **names; char *options; { X static char *procName = "NuExtract"; X X if (*options == 'p') { /* printing rather then extracting to file */ X print = TRUE; X dopack = TRUE; /* no extract uncompressed! */ X } else print = FALSE; X X Extract(filename, namecount, names); /* do stuff */ } X SHAR_EOF chmod 0644 nuext.c || echo 'restore of nuext.c failed' Wc_c="`wc -c < 'nuext.c'`" test 19160 -eq "$Wc_c" || echo 'nuext.c: original size 19160, current size' "$Wc_c" fi true || echo 'restore of numain.c failed' echo End of part 5, continue with part 6 exit 0 -- fadden@uts.amdahl.com (Andy McFadden) fadden@cory.berkeley.edu (expires in December) [ Above opinions are mine, Amdahl has nothing to do with them, etc, etc. ]