nulib/ 755 4170 1464 0 5500572254 5230 nulib/README 644 4170 1464 11432 5324372326 6220 NuLib v3.23 README Recent history: NuLib v3.24 (January 1993) - Improved MS-DOS filename fixing (now enforce "8 chars '.' 3 chars") - Fixed bug in LZW decompressor NuLib v3.23 (December 1992) - Minor bug fixes for the MS-DOS version (Dale G. Shields) NuLib v3.22 (September 1992) - Added "compress file as if it were a disk" code (provided by somebody whose name I just lost). Seems to work, but it's not 100% tested. - Faster compression - Updated filetype abbreviations NuLib v3.21 - Minor fixes NuLib v3.2 (April 1992): - Lots of minor bug fixes. (I was getting lost in the version numbers so I just upped it to 3.2). Of importance are the bug fixes to the LZW-II decoder and EFT (Expanded Fundamental Types) awareness. NuLib v3.14 (November 1991) - Finished fixing NeXT probs. Really. (Chris Osborn w/push from Bruce Kahn) - Fixed some minor bugs (Larry W. Virden) NuLib v3.13 - Repaired XENIX directory stuff in "nuadd.c" (Morgan Davis) NuLib v3.12 - Fixed problem with table clears on non-LZW blocks (Frank Petroski) NuLib v3.11 - Added some stuff to make the NeXT happy (Chris Osborn) NuLib v3.1 (October 1991) - GS/ShrinkIt LZW-II uncompression (finally!) - All ShrinkIt compression/uncompression routines are about 15% faster - Better compatibility with System V, especially the AT&T 3B2 Nulib v3.03 (February 1991) - Fixed XENIX problems with includes and libs (Ron Higgins). - Fixed bug in directory expansion (Larry W. Virden). NuLib v3.02 - Silenced screaming about bad dates (Larry W. Virden). - Fixed glitches in nulib.lnk and nulib.mak (Bruce Kahn). NuLib v3.01 - Fixed non-compression bug in ShrinkIt LZW (Scott Blackman). NuLib v3.0 (September 1990) - ShrinkIt LZW compression - UNIX 12-bit and 16-bit compression - New archive listing format (similar to ARC and ZOO) ============================================================================== Things work much better if you read the documentation file (now available on the archive site... sorry I didn't put it up sooner). If you want to use UNIX compress to store files, READ THE DOCUMENTATION FIRST. Not only does it tell you how to do it, but it has some warnings about compatibility. To compile this on a UNIX-type system, edit the file "nudefs.h". Several systems are predefined; the default is a BSD UNIX system. If you want to run this on something other than BSD, comment out the #define statements (using "/*" and "*/"), and uncomment the appropriate statements (several systems are defined... if yours is an AT&T System V system, try the defines for Amdahl UTS). Then, type "make" to execute the Makefile. If all goes well, you will be left with an executable file called "nulib". If all does not go well, double check "nudefs.h". You may need to deal with the HAS_EFT stuff, especially if your compiler complains that "mode_t" or "off_t" are undefined. Send some mail to me if you can't get it to work at all. To make the MS-DOS version, use the nulib.mak and nulib.lnk files with MS C (supplied by Bruce Kahn). To make the APW C version (NOT Orca/C), put all the files in one directory, and make a subdirectory called "OBJ". Put the "linked.scr" and "linker.scr" files in OBJ, and then "make.apw". This will compile all the files. When it finishes, change to OBJ and "alink linked.scr" (standard linker) or "alink linker.scr" (ZapLink). The makefile used to do this automatically, but with only 1.25MB of RAM you have to exit APW to purge all the memory used by the compiler, and and then restart APW and run the linker. In both cases, BE SURE to modify "nudefs.h" first. If you don't, the compilation will halt in "numain.c" on a line which reminds you to change "nudefs.h" (I tended to forget about this). Also, under APW make sure all the file types are set to SRC, and the auxtype to CC. These are changed with the commands "filetype" and "change", respectively. Please send bug reports, ideas, or gripes to fadden@uts.amdahl.com, or to one of the addresses mentioned in the documentation or under the "-hw" option. Known bugs: - Under some systems, using UNIX compress on a file which does not compress will cause the archive's EOF to be larger than it should be. This slack space will be used up if you add more files to the archive (with NuLib anyway; no guarantees about ShrinkIt or GS/ShrinkIt, but there's no reason why they shouldn't work). (just to make things clear: if the file being compressed doesn't get any smaller, the compression halts and the file is simply stored uncompressed. Because of the way compress works, on some systems the space that would have been occupied by more compressed data is left attached to the file. The ShrinkIt compression does not suffer from this problem. If you add more stuff to the file, NuLib will fill the slack space first, NOT just append to the end of the file). I can be reached at fadden@uts.amdahl.com ============================================================= Things work much better if you read the documentation file (now available on the archive site... sorry I didn't put it up sooner). If you want to use UNIX compress tonulib/NOTES 644 4170 1464 6710 5100656541 6132 Programming notes (05-Nov-89) (updated 23-Aug-90) ----- Name: What started out as a NuFX viewer turned into a full-blown archiver. It went from NuView to NuARC, which was reasonable... until I spoke to Andy Nicholas. Apparently he'd been warned against using the name NuARC, so I had to pick a new one. "CShrink" seemed the most rational, since it is coded entirely in C and is meant to complement ShrinkIt. However, L&L now owns the name "ShrinkIt", so I switched over to NuLib. My other choice was "NuPak", but that's now a completely different (but compatible) program. Sigh. A rose by any other name would be full of thorns. ----- Excuses: The code is written to be portable first, efficient second. If you see a way to retain portability across a *wide* range of machines (from a //gs to a Cray) but increase efficiency, please let me know. Because of the way this was developed (archive viewer -> simple extractor -> extractor with extra goodies, plus the NuFX standard kept changing while I was working), I haven't exactly followed top-down programming practices. It shows (wince). Some of the procedures are rather long. Good progamming practice dictates that no procedure should be longer than your screen; my screen (a Sun 3/50 running X10) has 62 lines at the moment. This is certainly no excuse for procedures in excess of 150 lines, but I have tried to break things down into pieces within the procedures themselves. Some program-wide globals are used; most are boolean values or some special control variables that have to be visible across several files. Probably the worst is "onebyt *pakbuf", which was used so that I didn't have to keep malloc()ing a 64K buffer every few calls. It should be malloc()ed ONLY by routines called directly from main(), and should be free()d at the end of those procedures (which should cause execution to go back to main()). Another bad one is tmpNameBuf. I was worried about having multiple 1K buffers filling static storage (or the stack), so I made this... it is intended to be *very* temporary; don't make any calls to other NuLib procedures and expect it to survive. This program is still under development... But it's getting there. ----- #ifdefs are generally structured as follows: #ifdef UNIX /* all UNIX systems come first */ # ifdef BSD43 ... # endif # ifdef SYSV ... # endif #else /* followed by micros, running GS/OS, MS-DOS, HFS, ... */ # ifdef APW ... # endif # ifdef MDOS ... # endif # ifndef APW /* if nothing else is defined... */ # ifndef MSDOS /* this is included so that somebody doing a port */ /* +PORT+ */ ... /* can easily figure out what needs to be altered */ # endif # endif #endif Things that need to be altered when porting between systems are tagged with "/* +PORT+ */" ----- #includes should be in this order: #include "nudefs.h" #include <...> #include "..." In ALL cases, nudefs.h should come first. Generally speaking it is a good idea to have the system includes before any NuLib ones. ----- UNIX doesn't really have a create_time field; it has accessed, modified, and "changed". Modified gets reset when the data gets modified; changed gets reset by chmod, chown, link, mknod, rename, unlink, utimes, write, and truncate. Anyway, the "modified" field is set what it should be, but I'm going to let the "changed" and "accessed" fields take care of themselves (in other words, I don't intend to modify them... besides, you can't change the "changed" field anyway). put it up sooner). If you want to use UNIX compress tonulib/Makefile 644 4170 1464 4015 5253501157 6754 # # UNIX Makefile for NuLib v3.2 # # To make a smaller executable, you can exclude the Binary II routines # by setting CFLAGS= -DNO_BLU # To exclude the UNIX compression routines, add -DNO_UCOMP # Under UTS 2.1, use -eft to make the linker use the EFT library. #EFT=-eft # Select appropriate flag... -g for debugging, -O for optimized. #CFLAGS=-g $(EFT) CFLAGS=-O -s $(EFT) #CFLAGS=-p $(EFT) HDRS=nudefs.h nuread.h nuview.h nuadd.h nuext.h nupdel.h nupak.h nuetc.h \ nublu.h nucomp.h nucompfn.h SRCS=numain.c nuread.c nuview.c nuadd.c nuext.c nupdel.c nupak.c nuetc.c \ nublu.c nucomp.c nushk.c nusq.c OBJS=numain.o nuread.o nuview.o nuadd.o nuext.o nupdel.o nupak.o nuetc.o \ nublu.o nucomp.o nushk.o nusq.o ARCFILES=README NOTES Makefile make.apw linker.scr linked.scr \ mkshk nulib.mak nulib.lnk *.h *.c LIBS= #LIBS= -lx # For XENIX/386 users CC=cc all: nulib nulib: ${OBJS} ${CC} ${CFLAGS} ${OBJS} -o nulib ${LIBS} # # .o targets # numain.o: numain.c nudefs.h nuread.h nuview.h nuadd.h nuext.h nupdel.h nublu.h\ nuetc.h nuread.o: nuread.c nudefs.h nuread.h nupak.h nuetc.h crc.h nuview.o: nuview.c nudefs.h nuview.h nuread.h nuetc.h nuadd.o: nuadd.c nudefs.h nuadd.h nuread.h nuadd.h nupak.h nuetc.h nuext.o: nuext.c nudefs.h nuext.h nuread.h nuext.h nupak.h nuetc.h nupdel.o: nupdel.c nudefs.h nupdel.h nuread.h nuadd.h nupak.h nupdel.h nuetc.h nupak.o: nupak.c nudefs.h nupak.h nuetc.h nucomp.h nucompfn.h nublu.o: nublu.c nudefs.h nublu.h nuview.h nuetc.h nushk.o: nushk.c nudefs.h nupak.h nusq.o: nusq.c nudefs.h nupak.h nuetc.o: nuetc.c nudefs.h nuetc.h nucomp.o: nucomp.c nudefs.h nucomp.h nucompfn.h nuetc.h # # other targets # saber: #load $(CFLAGS) $(SRCS) $(LIBS) # shar version 3.49 # -c : add "cut here" line at top # -o : base name for output files # -l48 : max size is 48KB, but don't split files # -v : (not used) turn off verbose msgs shar: shar349 -c -osh.files/nulib -l48 $(ARCFILES) tar: tar cvf nulib.tar $(ARCFILES) nulib.doc clean: rm -f $(OBJS) clobber: clean rm -f nulib uview.c nuadd.c nuext.c nupdel.c nupak.c nuetc.c \ nublu.c nucomp.c nushk.c nusq.c OBJS=numain.o nuread.o nuview.o nuadd.o nuext.o nupdel.o nupak.o nuetc.o \ nublu.o nucomp.o nushk.o nusq.o ARCFILES=README NOTES Makefile make.apw linker.scr linked.scr \ mkshk nulib.mak nulib.lnk *.h *.c LIBS= #LIBS= -lx # For XENIX/386 users CC=cc all: nulib nulib: ${OBJS} ${CC} ${CFLAGS} ${OBJS} -o nulib ${LIBS} # # .o targets # numain.o: numain.c nudefs.h nuread.h nuview.h nuadd.h nuext.h nupdenulib/make.apw 644 4170 1464 1203 5173642646 6750 * * NuLib AGE script (A pseudo-makefile for APW and ORCA) * for file in numain nuread nuview nuadd nuext nupdel nupak nuetc nublu nushk nusq nucomp age obj/{file}.root {file}.c if {age} != 0 echo compiling {file}.c delete obj/{file}.root compile {file}.c keep=obj/{file} end end * Once everything is compiled, I test the final * program file against the object modules that build it. * If linking is required, it is performed next followed * by a series of other statements to complete it: age cshk obj/=.root purge if {age} != 0 delete nulib prefix obj assemble linker.scr prefix .. end lKnusq.cKnuview.cK nulib.doc nusq.o ARCFILES=README NOTES Makefile make.apw linker.scr linked.scr \ mkshk nulib.mak nulib.lnk *.h *.c LIBS= #LIBS= -lx # For XENIX/386 users CC=cc all: nulib nulib: ${OBJS} ${CC} ${CFLAGS} ${OBJS} -o nulib ${LIBS} # # .o targets # numain.o: numain.c nudefs.h nuread.h nuview.h nuadd.h nuext.h nupdenulib/linker.scr 644 4170 1464 152 5173642506 7274 13/start numain nuread nuview nuadd nuext nupdel nuetc nublu nucomp nupak nushk nusq 13/clib keep=nulib lib.lnkKapwerr.hKcrc.hKnuadd.hKnublu.hKnucomp.h,K nucompfn.h@Knudefs.hPKnuetc.h`Knuext.hpKnupak.hKnupdel.hKnuread.hKnuview.hKnuadd.cKnublu.cKnucomp.cKnuetc.cKnuext.cnulib/linked.scr 644 4170 1464 4260 5163501371 7273 ; APW LinkEd script file for NuLib ; ; Does not search for segments with blank load segment names. ; Does not search files for DIRECT segments (only the CLIB). ; Does not search any libraries other than 2/clib. keep ../nulib * This is the main code segment (type = code) segment/$00 main loadselect 2/start.root main loadselect numain.root main loadselect nuread.root main loadselect nuetc.root main loadselect nuview.root main loadselect nuadd.root main loadselect nuext.root main loadselect nupdel.root main library/loadselect 2/clib main * This contains compression/Binary II code (type = dynamic) segment/dynamic Compress loadselect nupak.root main loadselect nublu.root main loadselect nucomp.root main loadselect nushk.root main loadselect nusq.root main library/loadselect 2/clib main * This contains all global definitions (type = private data) segment/$41 Globals loadselect 2/start.root ~globals loadselect numain.root ~globals loadselect nuread.root ~globals loadselect nuetc.root ~globals loadselect nuview.root ~globals loadselect nuadd.root ~globals loadselect nuext.root ~globals loadselect nupdel.root ~globals loadselect nupak.root ~globals loadselect nublu.root ~globals loadselect nucomp.root ~globals loadselect nushk.root ~globals loadselect nusq.root ~globals library/loadselect 2/clib ~globals * This holds all arrays (type = private data) segment/$41 Arrays loadselect 2/start.root ~arrays loadselect numain.root ~arrays loadselect nuread.root ~arrays loadselect nuetc.root ~arrays loadselect nuview.root ~arrays loadselect nuadd.root ~arrays loadselect nuext.root ~arrays loadselect nupdel.root ~arrays loadselect nupak.root ~arrays loadselect nublu.root ~arrays loadselect nucomp.root ~arrays loadselect nushk.root ~arrays loadselect nusq.root ~arrays library/loadselect 2/clib ~arrays * This has direct page stuff, like the stack segment/$12 Direct (type = direct-page/stack) library/loadselect 2/clib DIRECT list on * One other segment, SEGJPTBL, appears here... the file "nudefs.h". Several systems are predefined; the default is a BSD UNIX system. If you want to run this on something other than BSD, comment out the #define statements (using "/*" and "*/"), and uncomment the appropriate statements (several systems are defined... if yours is an AT&T System V system, try the defines for Amdahlnulib/mkshk 755 4170 1464 1341 5163501372 6355 #! /bin/csh # Make NuLib shk file for download. # Puts a copy of the files (without tabs and linefeeds) in ./trans, and then # adds them to "nulib.shk" # # ("detab" converts tabs to spaces) # # This is intended as an example; it may or may not work on your system. echo "--- Converting tabs and linefeeds" foreach file (README Benchmarks Makefile make.apw linker.scr linked.scr \ mkshk *.h *.c nulib.mak nulib.lnk) echo $file detab $file | tr '\012' '\015' >! trans/$file end cd trans nulib cvftxt/0000 nulib.shk README Benchmarks Makefile nulib.mak nulib.lnk nulib avfsrc/0006 nulib.shk make.apw mkshk nulib avfsrc/0009 nulib.shk linked.scr nulib avfsrc/0109 nulib.shk linker.scr nulib avfsrc/000a nulib.shk *.h *.c cd .. n loadselect nucomp.root main loadselect nushk.root main loadselect nusq.root main library/loadselect 2/clib main * This contains all global definitions (type = private data) segment/$41 Globals loadselect 2/start.root ~globals loadselect numain.root ~globanulib/nulib.mak 644 4170 1464 3321 5163742166 7125 # NuLib v3.02 makefile for Microsoft C #=================================================================== # # Standard command line definitions # # Note /DIAPX286 can also be used, but it is not a real indicator # of the system hardware # # Leaving out the /W3 will reduce the flood of warning messages. # #=================================================================== #cp=cl /c /AL /W3 /Ot /Zpei /DIAPX386 cp=cl /c /AL /W3 /Os /Zpei /DIAPX386 #=================================================================== # # Default inference rules # #=================================================================== .c.obj: $(cp) $*.c #=================================================================== # # Dependencies # #=================================================================== nuadd.obj: nuadd.c nudefs.h nuread.h nuview.h nuadd.h nupak.h nuetc.h nublu.obj: nublu.c nudefs.h nuview.h nuadd.h nupak.h nuetc.h nuetc.obj: nuetc.c nudefs.h apwerr.h nuetc.h nuext.obj: nuext.c nudefs.h nuread.h nuext.h nupak.h nuetc.h numain.obj: numain.c nudefs.h nuread.h nuview.h nuadd.h nuext.h nupdel.h \ nublu.h nupak.h nuetc.h nupak.obj: nupak.c nudefs.h nupak.h nuetc.h nucomp.h nucompfn.h nupdel.obj: nupdel.c nudefs.h nuread.h nuadd.h nupak.h nupdel.h nuetc.h nuread.obj: nuread.c nudefs.h crc.h nuread.h nupak.h nuetc.h nushk.obj: nushk.c nudefs.h nuread.h nupak.h nuetc.h nusq.obj: nusq.c nudefs.h nuetc.h nuview.obj: nuview.c nucomp.obj: nucomp.c nudefs.h nucomp.h nucompfn.h NULIB.exe: nuadd.obj nublu.obj nuetc.obj nuext.obj numain.obj nupak.obj \ nupdel.obj nuread.obj nushk.obj nusq.obj nuview.obj nucomp.obj link @NuLib.lnk ~arrays loadselect nuext.root ~arrays loadselect nupdel.root ~arrays loadselect nupak.root ~arrays loadselect nublu.root ~arrays loadselect nucomp.root ~arrays loadselect nushk.root ~arrays loadselect nusq.root ~arrays library/loadselect 2/clib ~arrays * Thisnulib/nulib.lnk 644 4170 1464 271 5163501373 7113 nuadd.obj+nublu.obj+nuetc.obj+nuext.obj+numain.obj+nupak.obj+nupdel.obj+ nuread.obj+nushk.obj+nusq.obj+nuview.obj+nucomp.obj NuLib NuLib.Map /STACK:10000 /NOI /EXE /FAR /PACKC ; Knuetc.h`Knuext.hpKnupak.hKnupdel.h Knuread.hKnuview.hN Knuadd.cKnublu.cKnucomp.cKnuetc.cKnuext.cKnumain.c $Knupak.c8Knupdel.c LKnuread.c \nulib/apwerr.h 644 4170 1464 15150 5253231344 7005 /* * apwerr.h - text versions of APW and ProDOS 16 error codes * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* * (ERROR() didn't cut it, and I'm trying to separate things from the shell.) */ /* APW-specific UNIX-like errors */ /* [ this is derived from: ] errno.h -- error return codes Copyright American Telephone & Telegraph Modified and used with permission, Apple Computer Inc. Copyright Apple Computer Inc. 1985, 1986, 1987 All rights reserved. */ /* @(#)errno.h 2.1 */ /* 3.0 SID # 1.3 */ #define sys_nerr 35 /* err must be < Max APW Err */ static char *sys_errlist[sys_nerr] = { /* 0 (no err) */ "[ call successful ]", /* 1 EPERM */ "permission denied", /* 2 ENOENT */ "no such file or directory", /* 3 ENORSRC */ "no such resource", /* 4 EINTR */ "interrupted system call", /* 5 EIO */ "I/O error", /* 6 ENXIO */ "no such device or address", /* 7 E2BIG */ "insufficient space for return argument", /* 8 ENOEXEC */ "exec format error", /* 9 EBADF */ "bad file number", /* 10 ECHILD */ "no children", /* 11 EAGAIN */ "no more processes", /* 12 ENOMEM */ "not enough memory", /* 13 EACCES */ "permission denied", /* 14 EFAULT */ "bad address", /* 15 ENOTBLK */ "block device required", /* 16 EBUSY */ "mount device busy", /* 17 EEXIST */ "file exists", /* 18 EXDEV */ "cross-device link", /* 19 ENODEV */ "no such device", /* 20 ENOTDIR */ "not a directory", /* 21 EISDIR */ "is a directory", /* 22 EINVAL */ "invalid argument", /* 23 ENFILE */ "file table overflow", /* 24 EMFILE */ "too many open files", /* 25 ENOTTY */ "not a typewriter (sorry)", /* 26 ETXTBSY */ "text file busy", /* 27 EFBIG */ "file too large", /* 28 ENOSPC */ "no space left on device", /* 29 ESPIPE */ "illegal seek", /* 30 EROFS */ "read only file system", /* 31 EMLINK */ "too many links", /* 32 EPIPE */ "broken pipe", /* 33 EDOM */ "math arg out of domain of func", /* 34 ERANGE */ "math result not representable" }; /* ProDOS errors */ /* [ This is derived from: ] /******************************************** ; File: ProDos.h ; ; ; Copyright Apple Computer, Inc. 1986, 1987 ; All Rights Reserved ; ********************************************/ #define MPErr 0x61 /* err must be < Max ProDOS Err # */ static char *ProDOSErr[MPErr] = { /* 00 (no error) */ "[ ProDOS call successful ]", /* 01 invalidCallNum */ "invalid call number / (fatal) unclaimed intr", /* 02 */ "", /* 03 */ "", /* 04 */ "(ProDOS 8 invalid parameter count)", /* 05 badPBlockPtr */ "call pointer out of bounds", /* 06 pdosActiveErr */ "ProDOS is active", /* 07 pdosBusyErr */ "ProDOS is busy", /* 08 */ "", /* 09 */ "", /* 0a vcbUnusable */ "(fatal) VCB is unusable", /* 0b fcbUnusable */ "(fatal) FCB is unusable", /* 0c badBlockZero */ "(fatal) block zero allocated illegally", /* 0d shdwInterruptErr */ "(fatal) interrupt occurred while I/O shadowing off", /* 0e */ "", /* 0f */ "", /* 10 devNotFound */ "device not found", /* 11 badDevRefNum */ "invalid device ref# / (fatal) wrong OS version", /* 12 */ "", /* 13 */ "", /* 14 */ "", /* 15 */ "", /* 16 */ "", /* 17 */ "", /* 18 */ "", /* 19 */ "", /* 1a */ "", /* 1b */ "", /* 1c */ "", /* 1d */ "", /* 1e */ "", /* 1f */ "", /* 20 badReqCode */ "invalid request code", /* 21 */ "", /* 22 */ "", /* 23 */ "", /* 24 */ "", /* 25 intTableFull */ "interrupt table full", /* 26 invalidOperation */ "invalid operation", /* 27 ioError */ "I/O error", /* 28 noDevConnect */ "no device connected", /* 29 */ "", /* 2a */ "", /* 2b writeProtectErr */ "write protect error", /* 2c */ "", /* 2d */ "", /* 2e diskSwitchErr */ "disk switched error", /* 2f */ "device not online", /* 30 */ "device-specific err $30", /* 31 */ "device-specific err $31", /* 32 */ "device-specific err $32", /* 33 */ "device-specific err $33", /* 34 */ "device-specific err $34", /* 35 */ "device-specific err $35", /* 36 */ "device-specific err $36", /* 37 */ "device-specific err $37", /* 38 */ "device-specific err $38", /* 39 */ "device-specific err $39", /* 3a */ "device-specific err $3a", /* 3b */ "device-specific err $3b", /* 3c */ "device-specific err $3c", /* 3d */ "device-specific err $3d", /* 3e */ "device-specific err $3e", /* 3f */ "device-specific err $3f", /* 40 badPathName */ "invalid pathname syntax", /* 41 */ "", /* 42 fcbFullErr */ "FCB full error (too many files open)", /* 43 badFileRefNum */ "invalid file reference number", /* 44 pathNotFound */ "path not found", /* 45 volumeNotFound */ "volume not found", /* 46 fileNotFound */ "file not found", /* 47 dupFileName */ "duplicate file name", /* 48 volumeFullErr */ "volume full error", /* 49 dirFullErr */ "directory full error", /* 4a versionErr */ "version error (incompatible file format)", /* 4b badStoreType */ "unsupported (or incorrect) storage type", /* 4c eofEncountered */ "end-of-file encountered", /* 4d positionRangeErr */ "position out of range", /* 4e accessErr */ "access not allowed", /* 4f */ "", /* 50 fileOpenErr */ "file already open", /* 51 dirDamaged */ "directory structure is damaged (file count?)", /* 52 badVolType */ "unsupported volume type", /* 53 paramRangeErr */ "parameter out of range", /* 54 memoryFullErr */ "out of memory", /* 55 vcbFullErr */ "VCB full error", /* 56 */ "(ProDOS 8 bad buffer address)", /* 57 dupVolumeErr */ "duplicate volume error", /* 58 notBlkDevErr */ "not a block device", /* 59 invalidLevel */ "invalid level", /* 5a */ "block number out of range (bad vol bitmap?)", /* 5b */ "illegal pathname change", /* 5c */ "not an executable file", /* 5d */ "file system not available", /* 5e */ "cannot deallocate /RAM", /* 5f */ "return stack overflow", /* 60 */ "data unavailable" }; id call number / (fatal) unclaimed intr", /* 02 */ "", /* 03 */ "", /* 04 */ "(ProDOS 8 invalid parameter count)", /* 05 badPBlockPtr */ "call pointer out of bounds", /* 06 pdosActiveErr */ "ProDOS is active", /* 07 pdosBusyErr */ "ProDOS is busy", /* 08 */ "", /* 09 */ "", /* 0a vcbUnusable */ "(fatal) VCB is unusable", /* 0nulib/crc.h 644 4170 1464 6364 5253231354 6244 /* * crc.h - tables for fast computation of 16-bit CRCS * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. * NOTE: First srgument must be in range 0 to 255. * Second argument is referenced twice. * * Programmers may incorporate any or all code into their programs, * giving proper credit within the source. Publication of the * source routines is permitted so long as proper credit is given * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, * Omen Technology. */ /*#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)*/ #define updcrc(cp, crc) ( (crctab[((crc >> 8) & 0xFF) ^ cp] ^ (crc << 8)) & 0xFFFF) /* crctab calculated by Mark G. Mendel, Network Systems Corporation */ static unsigned short crctab[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; , /* 05 badPBlockPtr */ "call pointer out of bounds", /* 06 pdosActiveErr */ "ProDOS is active", /* 07 pdosBusyErr */ "ProDOS is busy", /* 08 */ "", /* 09 */ "", /* 0a vcbUnusable */ "(fatal) VCB is unusable", /* 0nulib/nuadd.h 644 4170 1464 1667 5253231362 6570 /* * nuadd.h - declarations for nuadd.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* information is copied from file-dependent structures (FileRec) to here */ typedef struct { char *pathname; /* as much of the path as we need to know */ char *store_name; /* what name the file will be stored under */ fourbyt eof; /* length of file */ fourbyt fAccess; /* was Word */ fourbyt fileType; /* was Word */ fourbyt auxType; twobyt storageType; Time create_dt; /* Time = TimeRec = 8 bytes in misctool.h/nuread.h */ Time mod_dt; twobyt fileSysID; /* these two are non-standard */ onebyt fileSysInfo; int marked; /* application specific */ } file_info; #define MAXARGS 255 /* max #of files specified on command line; signed int */ extern void NuAdd(); extern long AddFile(); extern onebyt *MakeMHblock(); extern int EvalArgs(); ss; /* was Word */ fourbyt fileType; /* was Word */ fourbyt auxnulib/nublu.h 644 4170 1464 260 5253231370 6565 /* * nublu.h - declarations for nublu.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ extern void NuBNY(); Knuread.h Knuview.hOKnuadd.cKnublu.cKnucomp.cPKnuetc.cKnuext.cKnumain.cch$Knupak.c8Knupdel.c.LKnuread.c \Knushk.clKnusq.cKnuview.cK nulib.doc` auxnulib/nucomp.h 644 4170 1464 44553 5310761154 7020 /* * nucomp.h - declarations for nucomp.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef BSD43 # define NO_SETVBUF #endif #include #include #ifdef UNIX /* this is asking for trouble */ # include #else # ifdef IAPX286 # include # else # ifdef IAPX386 # include # else # include # endif # endif #endif #include #ifdef MSDOS # include #endif /*@H************************ < COMPRESS HEADER > **************************** * $@(#) compress.c,v 4.3 88/12/26 08:00:00 don Release ^ * * * * compress : compress.h * * * * port by : Donald J. Gloistein * * * * Source, Documentation, Object Code: * * released to Public Domain. This code is based on code as documented * * below in release notes. * * * *--------------------------- Module Description --------------------------* * THIS HEADER CONTAINS MUCH IMPLEMENTATION INFORMATION AND ASSUMPTIONS * * PLEASE PRINT IT OUT AND READ IT BEFORE COMPILING CODE FOR YOURSELF * * * * This header supports a number of compiler defines and predefines. * * Rather than explain all of them, please print the header and read the * * notes. Also the unix and xenix makefiles are commented for the * * various options. There continues to have a lot of Dos specific info in * * the header. This is to help on 16 bit Msdos machines to get their * * compiler to work properly. I make no appology for that, as this port * * began as a way to implement 16 bit compress on a segmented MsDos machine* * * * However, for Unix and Xenix, all you should have to define is -DXENIX * * or -DUNIX and compile. There may be a problem with whether your library * * supports alloc() or malloc(), but there is a define for that, also. * * * * This header can be maintained to keep up with the different compilers * * and systems. As distributed in don Release, the files will compile with * * no changes under Microsoft version 5.1 C compiler, and Xenix C compiler * * which is the Microsoft version 4 ported. If you are going to bind the * * code for use in MsDos and OS/2 machines, then you must uncomment the * * #define BIND in this header. Otherwise, this distribution of source * * detect Msdos and Xenix predefines from the compiler and adjust. * * * *--------------------------- Implementation Notes --------------------------* * * * compiled with : compress.fns * * * NOTE!!! Defaults of this code now are completely Unix, even for the * * msdos ports. That means that the program works as a filter, * * and will just sit there waiting for input from stdin if you * * issue just the command name. You must use -h or -? to get the * * full help screen now. Also, it will unlink (kill) as a default * * on successful compression and decompression. That means the * * source file will be erased. * * These defaults are changed with the FILTER and KEEPFLAG * * defines. * * * * NOTE!!! Compiler predefines were taken out of the compress.h header. * * You must either specify them on compile or uncomment the * * compiler define in this header. Compiling without doing these * * will result in a program that does unspecified actions. * * problems: * * The inpath and outpath is a bit kludged. It should work okay. * * Let me know if you have problems, especially under Unix. * * * * CAUTION: The bound version will run on Dos 2.x, but you must use the * * name compress.exe. If you rename the file, it will not run * * The unbound version will run on Dos 2.x with the name changed * * but due to the dos version, will not detect its own name. * * * * CAUTION: Non MsDos users. You must modify the _MAX_PATH defines for * * your operating system if it is different from the assumed * * standard. * * * * CAUTION: I have used a number of defines to make it possible to compile * * properly under a number of bit sizes and adjust for the memory * * allocation scheme needed. If you do not use a dos system, * * PLEASE pay attention to the defines for MAXSEG_64 and the one * * called SMALLMODEL. The SMALLMODEL define is set in the header * * but if you don't have a compiler that triggers the MAXSEG_64 * * define, you may end up with bad pointers. Becareful. * * * * Header for files using version 4 compress routines define MAIN * * in the file with defining instance of the global variables. * * There are a number of compilers for MsDos and Unix/Xenix. * * So the user must define the actions required. * * * * * * Defines: This header file contains most of the system wide defines. * * the purpose for this was to consolodate compiler differences * * into one area that is easily changed. * * * * define MAXBITS= if you want a different maximum bits. 16 bits will now * * run in about 400K of memory. * * define BIND if you are going to use Microsoft bind.exe program on the * * executable. * * * * define MSDOS if you are compiling under MsDos or PcDos and your compiler* * does not predefine it. * * * * Initials ---- Name --------------------------------- * * DjG Donald J. Gloistein, current port to MsDos 16 bit * * Plus many others, see rev.hst file for full list * * LvR Lyle V. Rains, many thanks for improved implementation * * of the compression and decompression routines. * *************************************************************************@H*/ #ifndef FALSE /* let's get some sense to this */ #define FALSE 0 #define TRUE !FALSE #endif #define NDEBUG #define NPROTO #define COMP40 /* take this out for a little more speed */ char *malloc(); #define ALLOCATE(x,y) malloc((unsigned int)x*y) #define FREEIT(ptr) free(ptr) #define NOSIGNAL /* what the hell is "SIGTYPE"? */ #define setbinary(fp) #define FAR #define CONST #define _MAX_DIR 64 /* FILTER if you want the program to operate as a unix type filter */ /* if not defined TRUE, then issuing command without parameters will */ /* print a usage and help information */ /* Use -DFILTER=0 to deactivate filter operation */ #ifndef FILTER #define FILTER FALSE #endif /* KEEPFLAG determines the default action on successful completion */ /* Unix convention is FALSE (erase input file) */ /* Use -DKEEPFLAG=1 to keep files as default or change here */ /* if you don't set it before here and you are compiling the debug */ /* version, then files will be kept. */ #ifndef KEEPFLAG #define KEEPFLAG TRUE #endif /* the following tells the system that the maximum segment is 64k */ /* if your compiler is not one of these and has this limitation */ /* Because of this, this code should compile with minimum porting */ /* in the COMPUSI.XEN module to most unix systems. */ /* This is also used to keep array indexing to 16 bit integer */ /* if not predefined in compiler implementation, you must define */ /* it separately if applicable to your compiler/system */ #define MAXSEG_64K /* put this in if you are compiling in small code */ /* model and your compiler does not predefine it */ /* this is for CPU' with 64k segment limitation. */ /* Use this define for small code, it is used by */ /* the header to decide on value for NEARHEAP */ /* #define SMALLMODEL */ /* does your system use far pointers ? if you want it enabled keep this */ /* if you have segment limit and compile in larger than 13 bits */ /* then you will have to use compact or large model if your compiler */ /* does not support far pointer keyword. */ #ifndef FAR #define FAR #endif /* What type does the alloc() function return, char or void? */ #ifndef ALLOCTYPE #define ALLOCTYPE char #endif /* Does your run time library support the ANSI functions for:*/ /* reverse string set search? strrpbrk() if not: */ #define NO_REVSEARCH /* Does your library include strrchr()? If not define this: */ /*#define NO_STRRCHR*//* unix/xenix module uses this function*/ /* Does your library include strchr()? If not define this: */ /*#define NO_STRCHR*//* dos module uses this function. */ /* definition for const key word if supported */ #ifndef CONST #define CONST #endif /* And now for some typedefs */ typedef unsigned short CODE; typedef unsigned char UCHAR; typedef unsigned int HASH; typedef int FLAG; /* * You can define the value of MAXBITS to be anything betweeen MINBITS * and MAXMAXBITS. This is will determine the maximum memory you will * use and how the tables will be handled. I recommend you just leave * it at MAXMAXBITS, because you can define DFLTBITS in compiling the * module COMPRESS.C to set the default, and you can vary the number * of bits at runtime by using the -b switch. */ /* * The only reason to change MAXBITS is if you absolutely must have * faster performance. If you specify 14 bits, the tables will not * be split; at 13 bits, you can fit in the MSDOS small memory model * and allocate tables in near heap. * This value is available to other modules through the variable maxbits. */ #define INITBITS 9 #define MINBITS 12 #define MAXMAXBITS 16 #ifndef MAXBITS #define MAXBITS MAXMAXBITS #endif #if (MAXBITS > MAXMAXBITS) #undef MAXBITS #define MAXBITS MAXMAXBITS #endif #if (MAXBITS < MINBITS) #undef MAXBITS #define MAXBITS MINBITS #endif /* You should define DFLTBITS to be the default compression code * bit length you desire on your system. * (I define mine in the compiler command line in my Makefile.LvR) * (I leave mine alone and keep to the maximum. DjG) */ #ifndef DFLTBITS #define DFLTBITS MAXBITS #endif #if (DFLTBITS < MINBITS) #undef DFLTBITS #define DFLTBITS MINBITS #endif #if (DFLTBITS > MAXBITS) #undef DFLTBITS #define DFLTBITS MAXBITS #endif /* correcting for different types of pointer arithmatic */ /* probably won't have to change it */ #define NULLPTR(type) ((type FAR *) NULL) /* in making this program portable the following allocation and */ /* free functions are called, with the following parameters: */ /* ALLOCTYPE FAR *emalloc(unsigned int x, int y) */ /* void efree(ALLOCTYPE FAR *ptr) */ /* you must define the allocation function and the free function */ /* keep in mind that the casts must be correct for your compiler */ /* NOTE these are the two functions to change for allocating pointers to */ /* far data space if you are not using Microsoft C v.5.1 */ /* Consult your compiler manual and find the low level function that */ /* returns a far pointer when compiled in the small model. */ /* if your compiler does not support that, you will have to compile with */ /* a model that defaults to far pointers to data (compact or large model)*/ /* HERE ARE SOME SAMPLE PREDEFINED ONES */ /* default allocation function, in segmented addressing, must return */ /* a far pointer or compile with far pointer data as default */ #ifndef ALLOCATE #include #define ALLOCATE(x,y) malloc((unsigned int)x*y) #define FREEIT(ptr) free((ptr)) #endif # ifdef MAXSEG_64K # if MAXBITS > 14 # define SPLIT_HT TRUE # else # define SPLIT_HT 0 # endif # else # define SPLIT_HT 0 # endif # ifdef MAXSEG_64K # if MAXBITS > 15 # define SPLIT_PFX TRUE # else # define SPLIT_PFX 0 # endif # else # define SPLIT_PFX 0 # endif #ifndef BUFSIZ #define BUFSIZ 512 #endif #ifdef NO_SETBUF #define NO_SETVBUF #endif /* NuLib: comment: this ought to use setbuffer() if available */ #ifdef NO_SETVBUF # ifndef NO_SETBUF # define setvbuf(fp,buf,mode,size) setbuf((fp),(buf)) # define ZBUFSIZE BUFSIZ # define XBUFSIZE BUFSIZ # else # define setvbuf(fp,buf,mode,size) # define ZBUFSIZE (1) # define XBUFSIZE (1) # endif #else # ifdef NEARHEAP # define XBUFSIZE (0xC00) # define ZBUFSIZE (0x1800) # else # define XBUFSIZE (0x3000) /* 12k bytes */ # define ZBUFSIZE (0x6000) /* 24k bytes */ # endif #endif #define UNUSED ((CODE)0) /* Indicates hash table value unused */ #define CLEAR ((CODE)256) /* Code requesting table to be cleared */ #define FIRSTFREE ((CODE)(CLEAR+1))/* First free code for token encoding */ #define MAXTOKLEN 512 /* Max chars in token; size of buffer */ #define OK 0 /* Result codes from functions: */ #define ERROR 1 #define NORMAL 0 #define SIGNAL_ERROR -1 /* signal function error */ #define NOMEM 2 /* Ran out of memory */ #define TOKTOOBIG 3 /* Token longer than MAXTOKLEN chars */ #define READERR 4 /* I/O error on input */ #define WRITEERR 5 /* I/O error on output */ #define INFILEBAD 6 /* Infile not in compressed format */ #define CODEBAD 7 /* Infile contained a bad token code */ #define TABLEBAD 8 /* The tables got corrupted (!) */ #define NOSAVING 9 /* no saving in file size */ #define NOTOPENED 10 /* output file couldn't be opened */ #define YES 1 #define NO 0 #include "nucompfn.h" /* This has to come late... needs typedefs above */ /* defines opening mode for files */ /* and suffixes for compressed file */ #define WRITE_FILE_TYPE FWRITE_STR /* NuLib: was "wb" */ #define READ_FILE_TYPE FREAD_STR /* NuLib: was "rb" */ #define SUFFIX ".Z" /* Defines for third byte of header */ #define BIT_MASK 0x1f #define BLOCK_MASK 0x80 /* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is a fourth header byte (for expansion). */ #define CHECK_GAP 10000L /* ratio check interval */ #ifdef MAIN UCHAR magic_header[] = { 0x1F,0x9D }; /* 1F 9D */ char rcs_ident[] = "@(#) compress,v 4.3 88/12/26 08:00:00 don Release $"; int overwrite = 0; /* Do not overwrite unless given -f flag */ int maxbits = DFLTBITS; /* user settable max # bits/code */ int exit_stat = 0; int keep = KEEPFLAG; /* True = don't kill file */ int keep_error = FALSE; /* True = keep output file even if error exist */ char *prog_name; char ifname[_MAX_DIR]; char inpath[_MAX_DIR]; char ofname [_MAX_DIR]; char outpath[_MAX_DIR]; int is_list = FALSE; /* flag for file parameters */ char endchar[1]; char xbuf[XBUFSIZE]; char zbuf[ZBUFSIZE]; char separator[] = "/"; int nomagic = FALSE; /* Use a 3-byte magic number header, unless old file */ int zcat_flg = TRUE; /* Write output on stdout, suppress messages */ int quiet = TRUE; /* don't tell me about compression */ /* * block compression parameters -- after all codes are used up, * and compression rate changes, start over. */ int block_compress = BLOCK_MASK; #ifdef COMP40 long int ratio = 0L; long checkpoint = CHECK_GAP; #endif /* force the overwrite */ int force = 0; #ifndef NDEBUG int verbose = FALSE; int debug = FALSE; #endif /* !NDEBUG */ int do_decomp = FALSE; #else /* not defining instance */ extern UCHAR magic_header[]; extern char rcs_ident[]; extern int overwrite; extern int maxbits; extern int exit_stat; extern int keep; extern int keep_error; extern char *prog_name; extern char inpath[]; extern char outpath[]; extern int is_list; extern char endchar[]; extern char xbuf[]; extern char zbuf[]; extern char ifname[]; extern char ofname[]; extern char separator[]; extern int nomagic; extern int zcat_flg; extern int quiet; extern int block_compress; #ifdef COMP40 extern long int ratio; extern long checkpoint; #endif extern int force; #ifndef NDEBUG extern int verbose; extern int debug; #endif /* !NDEBUG */ extern int do_decomp; #endif IZ # else # define setvbuf(fp,buf,mode,size) # define ZBUFSIZE (1) # define XBUFSIZE (1) # endif #else # ifdef NEARHEAP # nulib/nucompfn.h 644 4170 1464 4530 5253231404 7307 /* * nucompfn.h - function declarations for nucomp.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ extern int u_compress(), u_decompress(); /* COMPRESS.FNS global function declarations */ /* this should be compatible with any type of declaration for external functions. See compress.h for explaination */ #ifdef NPROTO extern void Usage(); extern int check_error(); extern char *name_index(); extern char *get_program_name(); #ifdef NO_STRCHR extern char *strchr(); #endif #ifdef NO_STRRCHR extern char *strrchr(); #endif #ifdef NO_REVSEARCH extern char *strrpbrk(); #endif extern int is_z_name(); extern int cl_block(); extern int make_z_name(); extern void unmake_z_name(); extern void compress(); extern void putcode(); extern void decompress(); extern CODE getcode(); extern void writeerr(); extern void copystat(); #ifndef NOSIGNAL extern int foreground(); extern SIGTYPE onintr(); extern SIGTYPE oops(); #endif extern void prratio(); extern void version(); #ifdef NEARHEAP extern ALLOCTYPE *emalloc(); extern void efree(); #else extern ALLOCTYPE FAR *emalloc(); extern void efree(); #endif extern int alloc_tables(); extern void init_tables(); extern int nextcode(); #else extern void Usage(int); extern int check_error(void); extern char *name_index(char *); extern int cl_block(void); extern char *get_program_name(char *); extern int is_z_name(char *); extern int make_z_name(char *); extern void unmake_z_name(char *); #ifdef NO_STRCHR extern char *strchr(char *,int); #endif #ifdef NO_STRRCHR extern char *strrchr(char *,int); #endif #ifdef NO_REVSEARCH extern char *strrpbrk(char *,char *); #endif extern void compress(void); extern void putcode(CODE,int); extern void decompress(void); extern CODE getcode(void); extern void writeerr(void); extern void copystat(char *,char *); #ifndef NOSIGNAL extern int foreground(void); extern SIGTYPE onintr(void); extern SIGTYPE oops(void); #endif extern void prratio(FILE *,long,long); extern void version(void); #ifdef NEARHEAP extern ALLOCTYPE *emalloc(unsigned int,int); extern void efree(ALLOCTYPE *); #else extern ALLOCTYPE FAR *emalloc(unsigned int,int); extern void efree(ALLOCTYPE FAR *); #endif extern int alloc_tables(CODE,HASH); extern void init_tables(void ); extern int nextcode(CODE *); #endif rth header byte (for expansion). */ #define CHECK_GAP 10000L /* ratio check interval */ #ifdef MAIN UCHAR magic_header[] = { 0x1F,0x9D }; /* 1F 9D */ char rcs_nulib/nudefs.h 644 4170 1464 12331 5310761100 6757 /* * nudefs.h - system-dependent typdefs, and global #defines and variables. * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* * IMPORTANT: This file must be first on the list of #includes, since some * include files will be processed based on these #defines */ /* SYSTEM DEPENDENCIES */ typedef unsigned char onebyt; typedef unsigned short twobyt; typedef unsigned long fourbyt; /* byte ordering; TRUE if high byte is first (68xxx), else FALSE (65xxx) */ extern int HiLo; /* actually part of numain.c */ /* Setup for Apple //gs APW */ /* [ "APW" is automatically defined by the compiler ] */ /* Setup for MS-DOS machines (80xxx based: IBM PC) */ /* #ifndef MSDOS */ /* #define MSDOS */ /* #endif */ /* Setup for AIX */ /* #define SYSV */ /* #define BSD_INCLUDES */ /* Setup for BSD UNIX systems */ /*#define UNIX*/ /*#define BSD43*/ /* Setup for the NeXT */ /* #define UNIX */ /* #define BSD43 */ /* #define NeXT */ /* (is this defined automatically?) */ /* Setup for XENIX/386 */ /* NOTE: if you get error messages about readdir() and opendir() during */ /* linking, remove the leading '#' from the line "#CLIBS= -lx" in */ /* "Makefile" */ /* #define XENIX386 */ /* #define UNIX */ /* #define SYSV */ /* #define NO_RENAME */ /* no rename() call in C library */ /* Setup for Amdahl UTS 2.1; also works on the AT&T 3B2 */ #define UNIX #define SYSV #define NO_RENAME /* (UTS only:) */ #define HAS_EFT /*(don't forget to add "-eft" flag in Makefile for UTS 2.1)*/ /* Setup for AOS/VS @ DG */ /* #define UNIX */ /* #define DATAGENERAL */ /* #define AOSVS */ /* Setup for AViiONs */ /* #define UNIX */ /* #define SYSV */ /* #define HAS_EFT */ /* Setup for other UNIX systems */ /* #define UNIX */ /* #define SYSV (or whatever) */ /* use table lookups to get CRCs */ #define CRC_TAB /* don't include Binary II */ /* #define NO_BLU */ /* don't include UNIX compress */ /* #define NO_UCOMP */ #ifdef UNIX /* (the #include setup for NuLib is pretty screwed up at this point...) */ # include /* need off_t, if it exists */ /* * With EFT, off_t is a longlong (8 bytes) and mode_t is four bytes (ulong). * They affect lseek() and open() calls. Without EFT, off_t is usually * four bytes, and mode_t is a short or an int. * * I'm not sure which systems don't have off_t or mode_t. BSD seems to have * off_t, but not mode_t; this'll have to be handled on a case-by-case basis. * Unfortunately #ifdefs don't pick up typedefs on all compilers... */ # ifndef HAS_EFT /*typedef long off_t;*/ /*typedef unsigned short mode_t;*/ # endif #else # ifndef HAS_EFT /* APW and MS-DOS should be the same */ typedef long off_t; typedef unsigned int mode_t; # endif #endif extern off_t lseek(); /*VERY important if lseek() doesn't return int*/ /* * The rest of this stuff shouldn't need to be changed */ /* * Some global defs */ /* errno wasn't defined in on some systems... */ #ifndef MSDOS extern int errno; #endif /* Maximum file name length that we intend to handle (watch stack size!) */ #define MAXFILENAME 1024 /* file operations */ #define S_ABS 0 /* seek absolute */ #define S_REL 1 /* seek relative */ #define S_END 2 /* seek from end */ #ifdef UNIX /* stuff for open() */ # define WPERMS 0644 /* read/write for owner, read only for others */ # define FREAD_STR "r" # define FWRITE_STR "w" # define O_BINARY 0 /* for non-UNIX open(); easier than #ifdefs */ #else # ifdef APW # define WPERMS 0666 /* read/write for all; this may not work for some */ # define FREAD_STR "rb" # define FWRITE_STR "wb" # endif # ifdef MSDOS # define S_IREAD 0000400 /* read permission, owner */ # define S_IWRITE 0000200 /* write permission, owner */ # define WPERMS S_IREAD | S_IWRITE # define FREAD_STR "rb" # define FWRITE_STR "wb" # endif # ifndef WPERMS /* other system */ # define WPERMS 0666 /* +PORT+ */ # define FREAD_STR "rb" # define FWRITE_STR "wb" # endif #endif /*UNIX*/ /* Time structure; same as TimeRec from misctool.h */ /* one-byte entries should not have alignment problems... */ typedef struct { onebyt second; onebyt minute; onebyt hour; onebyt year; onebyt day; onebyt month; onebyt extra; onebyt weekDay; } Time; /* * global to entire program */ extern int HiLo; /* byte ordering; FALSE on low-first (65816) */ extern int verbose; /* BOOLEAN: print verbose? */ extern int interact; /* BOOLEAN: interactive when overwriting? */ extern int dopack; /* BOOLEAN: do we want to pack/unpack? */ extern int doSubdir; /* BOOLEAN: expand subdirectories? */ extern int doExpand; /* BOOLEAN: expand archived filenames? */ extern int doMessages; /* BOOLEAN: do comments instead of data? */ extern int transfrom; /* how to do CR<->LF translation? (-1 = none) */ extern int transto; extern int packMethod; /* how to pack a file (thread_format) */ extern int diskData; /* BOOLEAN: store files as disk images */ extern fourbyt defFileType; /* default file type */ extern fourbyt defAuxType; /* default aux type */ extern onebyt *pakbuf; /* used by compression routines; created once to */ /* eliminate overhead involved in malloc()ing a 64K buffer */ extern char *prgName; /* for errors; don't like argv[0] */ efine XENIX386 */ /* #define UNIX */ /* #define SYSV */ /* #define NO_RENAME */ /* no rename() call in C library */ /* Setup for Amdahl UTS 2.1; also works on the AT&T 3B2 */ #define UNIX #define SYSV #define NO_RENAME /* (UTS only:) */ #define HAS_EFT /*(don't forget to add "-eft" flag in Mnulib/nuetc.h 644 4170 1464 2551 5253231420 6577 /* * nuetc.h - declarations for nuetc.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* * (this will be included by almost all source files; it should come last) */ /* define these if they haven't been already */ /* (typedef int BOOLEAN caused too many problems... #define is easier) */ #ifndef BOOLEAN # define BOOLEAN int #endif #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #ifdef UNIX # ifdef BSD43 # ifndef NeXT extern char *index(); /* BSD version */ extern char *rindex(); # endif # define INDEX(s, c) index(s, c) # define RINDEX(s, c) rindex(s, c) # else extern char *strchr(); /* AT&T version */ extern char *strrchr(); # define INDEX(s, c) strchr(s, c) # define RINDEX(s, c) strrchr(s, c) # endif #else extern char *strchr(); /* APW, MSC */ extern char *strrchr(); # define INDEX(s, c) strchr(s, c) # define RINDEX(s, c) strrchr(s, c) #endif extern char tmpNameBuf[]; /* external function declarations */ extern void Fatal(), Quit(); extern void free(); extern char *Malloc(); #ifdef APW extern void ToolErrChk(), perror(); #endif extern int strcasecmp(), strncasecmp(); extern void ArcfiCreate(), Rename(); extern BOOLEAN Exists(); extern char *MakeTemp(); extern void ExpandTime(); extern long ReduceTime(); extern Time *GetTime(); 1; also works on the AT&T 3B2 */ #define UNIX #define SYSV #define NO_RENAME /* (UTS only:) */ #define HAS_EFT /*(don't forget to add "-eft" flag in Mnulib/nuext.h 644 4170 1464 365 5253231426 6613 /* * nuext.h - declarations for nuext.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #define MAXDEPTH 64 /* max subdir depth we will unpack */ extern void NuExtract(); Knupak.c8Knupdel.cLKnuread.c\Knushk.clKnusq.cKnuview.c K nulib.doc endif #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #ifdef UNIX # ifdef BSD43 # ifndef NeXT extern chnulib/nupak.h 644 4170 1464 1556 5253231441 6606 /* * nupak.h - declarations for nupak.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* * This has function declarations for all of the pack routines; that way we * don't have to include .h files for all of the pack code. */ /* Pack/unpack buffer size; should be as big as read() & malloc() can take */ /* Note: must be AT LEAST 8200 bytes or things may break */ /* Bad things could happen if it's less than 12K */ #define PAKBUFSIZ 0xff80 extern long packedSize; extern onebyt lastseen; extern twobyt PackFile(); extern int UnpackFile(); /* BOOLEAN */ extern unsigned int crlf(); extern void Spin(), FCopy(); extern long pak_SHK(); /* pack P8 ShrinkIt format, in nushk.c */ extern void unpak_SQU(); /* unsqueeze, in nusq.c */ extern void unpak_SHK(); /* unShrink, in nushk.c */ strrchr(); # define INDEX(s, c) strchr(s, c) # define RINDEX(s, c) strrchr(s, c) #endif extern char tmpNameBuf[]; /* external function declaratnulib/nupdel.h 644 4170 1464 324 5253231447 6735 /* * nupdel.h - declarations for NuUpdate and NuDelete * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ extern void NuDelete(), NuUpdate(); nupak.c8Knupdel.c" LKnuread.c\Knushk.clKnusq.c?Knuview.c *K nulib.doc6malloc() can take */ /* Note: must be AT LEAST 8200 bytes or things may break */ /* Bad things could happen if it's less than 12K */ #define PAKBUFSnulib/nuread.h 644 4170 1464 10126 5253231456 6765 /* * nuread.h - linked list structures used for holding NuFX header data, * and structure definitions for archive innards * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* * (this will be included by all source files which access NuFX archives) */ /* The NuFX master format version we output, and the maximum we can extract */ #define OURMVERS 2 #define MAXMVERS 2 /* The NuFX record format version we output, and the maximum we can extract */ #define OURVERS 0 #define MAXVERS 3 /* "NuFile" in alternating ASCII */ static onebyt MasterID[7] = { 0x4e, 0xf5, 0x46, 0xe9, 0x6c, 0xe5, 0x0 }; /* "NuFX" in alternating ASCII */ static onebyt RecordID[5] = { 0x4e, 0xf5, 0x46, 0xd8, 0x0 }; /* * Structure definitions for NuFX innards */ /* master header block */ typedef struct { onebyt ID[6]; twobyt master_crc; fourbyt total_records; Time arc_create_when; Time arc_mod_when; twobyt master_version; onebyt reserved1[8]; fourbyt master_eof; onebyt reserved2[6]; } MHblock; #define MHsize 48 /* this should not change */ /* record header block */ typedef struct { onebyt ID[4]; twobyt header_crc; twobyt attrib_count; twobyt version_number; twobyt total_threads; twobyt reserved1; twobyt file_sys_id; onebyt file_sys_info; onebyt reserved2; fourbyt access; fourbyt file_type; fourbyt extra_type; twobyt storage_type; Time create_when; Time mod_when; Time archive_when; twobyt option_size; /* future expansion here... */ } RHblock; #define RHsize 58 /* sizeof(RHblock) should work, but might not */ #define ATTSIZE 64 /* default attrib_count when creating new */ /* * This buffer must be able to contain three things (not all at once): * - The master header block (size = MHsize) * - The record header block (size = RHsize) * - Attributes not specified in the RHblock (attrib_count - RHsize - 2) * * Currently, it only needs to be 64 bytes. Since it is allocated as local * storage only once during execution, making it reasonably large should * not cause any problems in performance but will make the program stable * if it encounters an archive with a drastically expanded RHblock. */ #define RECBUFSIZ 256 /* thread record */ typedef struct { twobyt thread_class; twobyt thread_format; twobyt thread_kind; twobyt thread_crc; fourbyt thread_eof; fourbyt comp_thread_eof; } THblock; #define THsize 16 /* this should not change */ /* * Definitions for the linked lists * A linked list of Record headers, with linked lists of Threads attached */ /* thread nodes */ typedef struct TNode_s { THblock *THptr; /* points to thread info */ long fileposn; /* absolute position of this thread in the file */ struct TNode_s *TNext; /* points to next thread node */ } TNode; /* record nodes */ typedef struct RNode_s { RHblock *RHptr; /* points to the record header block */ char *filename; /* filename of record */ twobyt filename_length; /* length of filename (as stored in record hdr) */ twobyt real_fn_length; /* length of filename (actual) */ TNode *TNodePtr; /* points to first thread node */ fourbyt unc_len; /* total uncompressed length of all threads */ fourbyt comp_len; /* total compressed length of all threads */ struct RNode_s *RNext; /* points to next record node */ } RNode; /* head of list */ typedef struct { char *arc_name; /* filename of archive */ MHblock *MHptr; /* points to master header */ RNode *RNodePtr; /* points to first record node */ long nextposn; /* abs. position in file to put next record (for ADD) */ } ListHdr; /* * function declarations */ extern ListHdr *NuRead(); /* read archive info into memory */ extern void NuTest(); /* archive integrity check */ extern twobyt CalcCRC(); /* calculate a CRC on a range of bytes */ extern char *PrintDate(); /* print a date from a (Time *) struct */ extern void BCopy(); /* copy bytes: *src, *dest, num, order? */ extern void HiSwap(); /* swap bytes (maybe): *ptr, src_index, dst_index */ MSDOS # define S_IREAD 0000400 /* read permission, owner */ # define S_IWRITE 0000200 /* write permission, owner */ # define WPERMS S_IREAD | S_IWRITE # define FREAD_STR "rb" # define FWRITE_STR "wb" # endif # ifndef WPERMS /* other system */ # define WPERMS 0666 /* +PORT+ */ # define FREAD_STR "rb" # define FWRITE_STR "wb" # endif #endif /*UNIX*/ /* Time structure; same as TimeRec from misnulib/nuview.h 644 4170 1464 1161 5253231472 7001 /* * nuview.h - declarations for nuview.c * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ typedef enum { NAMEONLY, PROSHK, ARCZOO, FULL } outtype; /* constant string declarations */ extern char *unknownStr; extern char *WD[]; extern char *MO[]; #define TCn 4 extern char *TC[]; #define TKn 3 extern char *TK[][TKn]; #define TFn 6 extern char *TF[]; #define BTFn 6 extern char *BTF[]; #define QTFn 6 extern char *QTF[]; #define FIDn 14 extern char *FID[]; #define STn 14 extern char *ST[]; extern char *FT[]; extern void NuView(); extern char *PrintDate(); ecord header block */ typedef struct { onebyt ID[4]; twobyt header_crc; twobyt attrib_count; twobyt version_number; twobyt total_threads; twobyt reserved1; twobyt file_sys_id; onebyt file_sys_info; onebyt reserved2; fourbyt access; fourbyt file_type; fourbyt extra_type; twobyt storage_type; Time create_when; Time mod_when; Time archinulib/nuadd.c 644 4170 1464 53257 5310761471 6610 /* * nuadd.c - operations which add to a NuFX archive * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "NuMain" #endif #include "nudefs.h" #include #include #include #include #ifdef BSD43 # include #else # include #endif #ifdef UNIX # include # include # ifdef SYSV # ifdef XENIX386 # include /* maybe , , ...*/ # else /*other SYSV*/ # include # endif # endif # ifdef BSD43 # include # endif #endif #ifdef APW # include # include # include # include #endif #ifdef MSDOS # include # include # include # include # include # include #endif #include "nuread.h" #include "nuadd.h" #include "nupak.h" #include "nuetc.h" #ifdef DATAGENERAL /* BAK */ # ifdef AOSVS /* BAK */ # define BROKEN_ON_MVs /* MV/UX is NOT a full UNIX */ # endif /* implem. so we just skip */ #endif /* the 'UNIX' code on MVs */ #define MAXGSPREFIX 64 static BOOLEAN domove; /* are we M)oving the files in? */ static BOOLEAN docreate; /* using the 'C' option? */ /* * Expand command args into filenames * Stuff number of names into int; build File Information Array. * (this routine is heavily implementation-specific, since no two systems * expand wildcards or deal with subdirectories in the same way). * * Recursively expands subdirectories, unless doSubdir is FALSE. */ int EvalArgs(count, names, FIArray, first) int count; /* #of filenames */ char **names; /* array of file names */ file_info *FIArray[]; /* array to fill with file info */ BOOLEAN first; /* first time through? */ { static char *procName = "EvalArgs"; #ifdef UNIX /* UNIX shells (sh, csh) won't expand subdirectories, but they do * expand wildcards in arguments before we get them */ static int idx; struct stat st; char *cp; /* temp char pointer */ /* dir stuff */ int newcount; char **newnames; #ifndef BROKEN_ON_MVs DIR *dirp; #endif #ifdef SYSV # ifdef XENIX386 struct direct *dp; /* XENIX is like SYSV but with BSD dir stuff */ # else struct dirent *dp; # endif #else struct direct *dp; #endif /*SYSV*/ int nmlen; if (first) idx = 0; while (count--) { FIArray[idx] = (file_info *) Malloc(sizeof(file_info)); if (stat(*names, &st) < 0) { /* get file info */ if (errno == ENOENT) { fprintf(stderr, "%s: '%s' not found\n", prgName, *names); names++; continue; /* with while */ } Fatal("Bad stat()", procName); } if ((st.st_mode & S_IFDIR) && doSubdir) { /* is it a directory? */ # ifndef BROKEN_ON_MVs newnames = (char **) Malloc(MAXARGS * sizeof(char *)); strcpy(tmpNameBuf, *names); /* earlier dir stuff */ strcat(tmpNameBuf, "/"); nmlen = strlen(tmpNameBuf); if ((dirp = opendir(*names)) == NULL) Fatal("Unable to open subdirectory", procName); for (newcount=0, dp=readdir(dirp); dp != NULL; dp=readdir(dirp)) { if ((!strcmp(dp->d_name, ".")) || (!strcmp(dp->d_name, ".."))) continue; /* with for */ #ifdef SYSV newnames[newcount] = (char *)Malloc(nmlen+strlen(dp->d_name)+1); #else newnames[newcount] = (char *)Malloc(nmlen + dp->d_namlen +1); #endif strcpy(newnames[newcount], tmpNameBuf); strcat(newnames[newcount], dp->d_name); /* append the name */ newcount++; } closedir(dirp); EvalArgs(newcount, newnames, FIArray, FALSE); /* do subdir */ while (newcount-- > 0) /* free up the space we allocated */ free(newnames[newcount]); free(newnames); names++; # else /* BAK */ printf("Help, I ran into a directory and can't handle it!\n"); # endif /* BAK */ } else if ((st.st_mode & S_IFDIR) && !doSubdir) { /* maybe print message? */ names++; continue; /* with while */ } else if (st.st_mode & S_IFREG) { FIArray[idx]->eof = (long) st.st_size; if (st.st_mode & S_IWRITE) /* write permission enabled? */ FIArray[idx]->fAccess = (fourbyt) 0x00e3; /* locked */ else FIArray[idx]->fAccess = (fourbyt) 0x0021; /* unlocked */ FIArray[idx]->fileType = defFileType; FIArray[idx]->auxType = diskData ? st.st_size/512 : defAuxType; FIArray[idx]->fileSysID = 0x0001; /* ProDOS */ FIArray[idx]->fileSysInfo = 0x2f; /* '/' */ ExpandTime(&st.st_mtime, &FIArray[idx]->create_dt); /*use mod.. */ ExpandTime(&st.st_mtime, &FIArray[idx]->mod_dt); /*time for both */ FIArray[idx]->marked = FALSE; FIArray[idx]->pathname = (char *) Malloc(strlen(*names)+1); strcpy(FIArray[idx]->pathname, *names); FIArray[idx]->store_name = (char *) Malloc(strlen(*names)+1); cp = *names; while (*cp == '/') cp++; /* advance past leading '/' */ strcpy(FIArray[idx]->store_name, cp); /* can't otherwise fix */ names++; idx++; } else { printf("Unknown storage type for '%s'\n", *names); names++; continue; /* with while */ } } return (idx); #else /* UNIX */ # ifdef APW static int idx; /* will eventually hold the total #of filenames */ char *nextname = (char *) Malloc(MAXFILENAME); /* for subdir expand */ char prefix[MAXGSPREFIX+1]; /* Max ProDOS prefix size; now 64 */ char *fnptr; FileRec finfo_p; PrefixRec prefix_p; OpenRec open_p; EOFRec eof_p; if (first) idx = 0; prefix_p.prefixNum = 0; /* current dir */ prefix_p.prefix = prefix; /* prefix buffer */ GET_PREFIX( &prefix_p ); ToolErrChk(); p2cstr(prefix); while (count) { strcpy(tmpNameBuf, *names); c2pstr(tmpNameBuf); INIT_WILDCARD(tmpNameBuf, 0); ToolErrChk(); while (*NEXT_WILDCARD(tmpNameBuf)) { if (idx >= MAXARGS) { fprintf(stderr, "Too many files (%d, %d max)\n", idx, MAXARGS); Quit (-1); } finfo_p.pathname = tmpNameBuf; GET_FILE_INFO( &finfo_p ); ToolErrChk(); open_p.openPathname = tmpNameBuf; OPEN( &open_p ); ToolErrChk(); eof_p.eofRefNum = open_p.openRefNum; GET_EOF( &eof_p ); ToolErrChk(); CLOSE( &open_p ); ToolErrChk(); p2cstr(tmpNameBuf); /* also does p2cstr(finfo_p.pathname) */ switch (finfo_p.storageType) { case 0x00: /* standard ProDOS storage types */ case 0x01: case 0x02: case 0x03: FIArray[idx] = (file_info *) Malloc(sizeof(file_info)); FIArray[idx]->eof = eof_p.eofPosition; FIArray[idx]->fAccess = finfo_p.fAccess; FIArray[idx]->fileType = (fourbyt) finfo_p.fileType; FIArray[idx]->auxType = (fourbyt) finfo_p.auxType; FIArray[idx]->storageType = finfo_p.storageType; FIArray[idx]->fileSysID = 0x0001; /* ProDOS */ FIArray[idx]->fileSysInfo = 0x2f; /* '/' */ ExpandTime(&finfo_p.createDate, &FIArray[idx]->create_dt); ExpandTime(&finfo_p.modDate, &FIArray[idx]->mod_dt); FIArray[idx]->marked = FALSE; FIArray[idx]->pathname = (char *) Malloc(strlen(tmpNameBuf)+1); strcpy(FIArray[idx]->pathname, tmpNameBuf); /* are we adding from current directory? */ if (!strncmp(tmpNameBuf, prefix, strlen(prefix))) { FIArray[idx]->store_name = /* yes */ (char *) Malloc(strlen(tmpNameBuf) - strlen(prefix) +1); strcpy(FIArray[idx]->store_name, tmpNameBuf+ strlen(prefix)); } else { fnptr = RINDEX(tmpNameBuf, '/') + 1; /* no */ FIArray[idx]->store_name = (char *)Malloc(strlen(fnptr)+1); strcpy(FIArray[idx]->store_name, fnptr); } idx++; break; case 0x05: printf("Can't handle Extended file '%s'\n", tmpNameBuf); break; case 0x0d: if (doSubdir) { strcpy(nextname, tmpNameBuf); /* make new copy */ strcat(nextname, "/="); /* APW-only wildcard */ EvalArgs(1, &nextname, FIArray, FALSE); /* read subdir */ } break; default: printf("Unknown storage type for '%s'\n", tmpNameBuf); break; } } /* inner while */ names++, count--; } /* outer while */ free (nextname); return (idx); # endif /* APW */ # ifdef MSDOS /* MS-DOS or other shell wildcard expansion here */ int idx, error; struct stat fStat; idx = 0; while (count--) { error = stat (*names, &fStat); /* If the filename is a directory, we need to expand that too! */ if (!error) { FIArray[idx] = (file_info *) Malloc(sizeof(file_info)); FIArray[idx]->pathname = (char *) Malloc(strlen(*names)+1); strcpy(FIArray[idx]->pathname, *names); FIArray[idx]->store_name = (char *) Malloc(strlen(*names)+1); strcpy(FIArray[idx]->store_name, *names); FIArray[idx]->fAccess = 0x00e3L; /* unlocked */ FIArray[idx]->fileType = defFileType; FIArray[idx]->auxType = diskData ? fStat.st_size/512:defAuxType; FIArray[idx]->storageType = 0x0000; FIArray[idx]->fileSysID = 0x0001; /* ProDOS */ FIArray[idx]->fileSysInfo = 0x1c; /* '\' */ ExpandTime(&fStat.st_ctime, &FIArray[idx]->create_dt); ExpandTime(&fStat.st_mtime, &FIArray[idx]->mod_dt); FIArray[idx]->eof = fStat.st_size; FIArray[idx]->marked = FALSE; idx++; } names++; } return (idx); # endif /* MDOS */ # ifndef APW # ifndef MSDOS /* nothing else defined */ /* +PORT+ */ printf("\n[other] wildcard expansion/file info needs work\n"); while (count--) { FIArray[count] = (file_info *) Malloc(sizeof(file_info)); FIArray[count]->pathname = (char *) Malloc(strlen(*names)+1); strcpy(FIArray[count]->pathname, *names); FIArray[count]->store_name = (char *) Malloc(strlen(*names)+1); strcpy(FIArray[count]->store_name, *names); FIArray[count]->fAccess = 0x00e3L; /* unlocked */ FIArray[count]->fileType = 0x0006L; /* BIN */ FIArray[count]->auxType = 0L; FIArray[count]->storageType = 0x0000; FIArray[count]->fileSysID = 0x0001; /* ProDOS */ FIArray[count]->fileSysInfo = 0x1c; /* '\' */ ExpandTime((char *) NULL, &FIArray[count]->create_dt); ExpandTime((char *) NULL, &FIArray[count]->mod_dt); FIArray[count]->marked = FALSE; names++; } return (count); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } /* * Add a file onto the end of an archive; does not check to see if an entry * already exists. * * This creates the record entry, and calls subroutines to add the various * threads. The archive fd should be open, the file fd should not. Returns * the size of the record added. */ long AddFile(arcfd, infoptr) int arcfd; file_info *infoptr; { int srcfd; /* file to add */ onebyt *recBuf; /* record header block */ twobyt *twoptr; THblock thread[1]; /* thread block */ twobyt CRC; int idx; fourbyt total_threads; long recposn; /* file posn for record entry */ long thposn; /* file posn for last thread */ long tmpposn; /* temporary file posn */ static char *procName = "AddFile"; if (verbose) { printf("Adding '%s' (data)...", infoptr->store_name, infoptr->eof); fflush(stdout); } recBuf = (onebyt *) Malloc(ATTSIZE); for (idx = 0; idx < ATTSIZE; idx++) /* zero the buffer */ *(recBuf+idx) = 0; total_threads = 0; strncpy((char *) recBuf+0, (char *) RecordID, 4); twoptr = (twobyt *) (recBuf+6); *twoptr = ATTSIZE; /* don't have an attrib_count... */ HiSwap((onebyt *) recBuf, 6, 7); twoptr = (twobyt *) (recBuf+8); *twoptr = OURVERS; /* store new record with our rec vers */ HiSwap((onebyt *) recBuf, 8, 9); /* total_threads */ /* BCopy((onebyt *) &total_threads, (onebyt *) recBuf+10, 2, TRUE); */ *(recBuf+12) = 0; /* reserved1 */ *(recBuf+13) = 0; BCopy((onebyt *) &infoptr->fileSysID, (onebyt *) recBuf+14, 2, TRUE); BCopy((onebyt *) &infoptr->fileSysInfo, (onebyt *) recBuf+16, 1, TRUE); *(recBuf+17) = 0; /* reserved2 */ BCopy((onebyt *) &infoptr->fAccess, (onebyt *) recBuf+18, 4, TRUE); BCopy((onebyt *) &infoptr->fileType, (onebyt *) recBuf+22, 4, TRUE); BCopy((onebyt *) &infoptr->auxType, (onebyt *) recBuf+26, 4, TRUE); BCopy((onebyt *) &infoptr->create_dt, (onebyt *) recBuf+32, sizeof(Time), FALSE); BCopy((onebyt *) &infoptr->mod_dt, (onebyt *) recBuf+40, sizeof(Time), FALSE); BCopy((onebyt *) GetTime(), (onebyt *) recBuf+48, sizeof(Time), FALSE); twoptr = (twobyt *) (recBuf + (ATTSIZE - 2)); *twoptr = strlen(infoptr->store_name); /* correct strlen ordering */ HiSwap((onebyt *) recBuf, ATTSIZE-2, ATTSIZE-1); thread[0].thread_class = 0x0002; /* data */ HiSwap((onebyt *) &thread[0].thread_class, 0, 1); thread[0].thread_kind = diskData ? 0x0001 : 0x0000; /* disk or data fork */ HiSwap((onebyt *) &thread[0].thread_kind, 0, 1); thread[0].thread_format = 0x0000; /* filled in later */ thread[0].thread_crc = 0x0000; /* not supported yet */ /* so I don't forget if I support these */ HiSwap((onebyt *) &thread[0].thread_crc, 0, 1); thread[0].thread_eof = infoptr->eof; HiSwap((onebyt *) &thread[0].thread_eof, 0, 3); HiSwap((onebyt *) &thread[0].thread_eof, 1, 2); thread[0].comp_thread_eof = -1L; /* filled in later */ total_threads++; BCopy((onebyt *) &total_threads, (onebyt *) recBuf+10, 4, TRUE); /* * Because we don't know CRCs or compressed size yet, we must: * skip record entry and filename. * for each thread: * skip thread entry, write data, move back, write thread entry. * move back, write record entry and filename. * move forward to next position. */ if ((srcfd = open(infoptr->pathname, O_RDONLY | O_BINARY)) < 0) Fatal("Unable to open file", procName); recposn = lseek(arcfd, (off_t) 0, S_REL); /* save record posn */ if (lseek(arcfd, (off_t) (ATTSIZE + strlen(infoptr->store_name)), S_REL)<0) Fatal("Bad seek (R.rel)", procName); /* loop... */ thposn = lseek(arcfd, (off_t) 0, S_REL); /* save thread posn */ if (lseek(arcfd, (off_t) THsize, S_REL) < 0) Fatal("Bad seek (Th)", procName); /* * since we can store files as being packed without actually packing them, * we need to check "dopack" to see if we want packMethod or zero. Note * that packing can fail for various reasons; the value returned by * PackFile() is the actual algorithm used to pack the file. * * NuLib uses version 0 records; thread_crcs are not stored. */ thread[0].thread_format = PackFile(srcfd,arcfd, infoptr->eof, dopack ? packMethod:0, pakbuf); if (!dopack) thread[0].thread_format = packMethod; /* for S subopt */ HiSwap((onebyt *) &thread[0].thread_format, 0, 1); thread[0].comp_thread_eof = (fourbyt) packedSize; HiSwap((onebyt *) &thread[0].comp_thread_eof, 0, 3); /* correct ordering*/ HiSwap((onebyt *) &thread[0].comp_thread_eof, 1, 2); tmpposn = lseek(arcfd, (off_t) 0, S_REL); if (lseek(arcfd, (off_t) thposn, S_ABS) < 0) /* seek back to thread posn */ Fatal("Bad seek (Th2)", procName); if (write(arcfd, &thread[0], THsize) < THsize) /* write updated thread */ Fatal("Unable to write thread", procName); if (lseek(arcfd, (off_t)tmpposn, S_ABS) < 0) /*seek back to where we were*/ Fatal("Bad seek (TmpA)", procName); /* ...loop end */ if (close(srcfd) < 0) Fatal("Unable to close file", procName); CRC = CalcCRC(0, (onebyt *) recBuf+6, ATTSIZE-6); CRC = CalcCRC(CRC, (onebyt *) infoptr->store_name, strlen(infoptr->store_name)); CRC = CalcCRC(CRC, (onebyt *) &thread[0], THsize); twoptr = (twobyt *) (recBuf+4); *twoptr = CRC; HiSwap((onebyt *) recBuf, 4, 5); tmpposn = lseek(arcfd, (off_t) 0, S_REL); /* record posn (next posn) */ if (lseek(arcfd, (off_t)recposn, S_ABS) < 0) /* seek back to record entry */ Fatal("Bad seek (R.abs)", procName); if (write(arcfd, recBuf, ATTSIZE) < ATTSIZE) Fatal("Unable to write record", procName); if (write(arcfd, infoptr->store_name, strlen(infoptr->store_name)) < strlen(infoptr->store_name)) Fatal("Unable to store filename", procName); if (lseek(arcfd, (off_t)tmpposn, S_ABS) < 0) /*seek back to where we were */ Fatal("Bad seek (TmpB)", procName); if (verbose) printf("done.\n"); free(recBuf); /* switch ordering back */ HiSwap((onebyt *) &thread[0].comp_thread_eof, 0, 3); HiSwap((onebyt *) &thread[0].comp_thread_eof, 1, 2); return ( (long) (THsize * total_threads) + (long) ATTSIZE + (long) strlen(infoptr->store_name) + thread[0].comp_thread_eof); } /* * Certain options can cause an archive to be created (add, create, move). * If the archive does not already exist, then an empty file is created (of * type $e0/8002 under ProDOS) and an empty archive struct is built. * * Note that this requires certain options to deal with archive structures that * do not have any records, and archive files that are empty. * * If the file exists, this will call NuRead() to read it; otherwise, it will * create it. */ static ListHdr * CreateMaybe(filename) char *filename; { ListHdr *archive; MHblock *MHptr; onebyt *bufPtr; twobyt *twoptr; fourbyt *fourptr; int idx; #ifdef APW FileRec create_p; #endif static char *procName = "CreateMaybe"; if (Exists(filename)) { archive = NuRead(filename); return (archive); } if (!docreate) printf("Archive does not exist; creating archive file...\n"); archive = (ListHdr *) Malloc(sizeof(ListHdr)); archive->arc_name = (char *) Malloc(strlen(filename)+1); strcpy(archive->arc_name, filename); archive->MHptr = (MHblock *) Malloc(sizeof(MHblock)); archive->RNodePtr = (RNode *) NULL; archive->nextposn = (long) MHsize; bufPtr = (onebyt *) archive->MHptr; for (idx = 0; idx < MHsize; idx++) *(bufPtr+idx) = '\0'; /* total_records -> zero */ MHptr = archive->MHptr; strncpy((char *) MHptr->ID, (char *) MasterID, 6); BCopy((onebyt *) GetTime(), (onebyt *) &(MHptr->arc_create_when),8, FALSE); BCopy((onebyt *) bufPtr+12, (onebyt *) &(MHptr->arc_mod_when), 8, FALSE); fourptr = (fourbyt *) (&(MHptr->master_eof)); *fourptr = (fourbyt) MHsize; /* twoptr = (twobyt *) (&(MHptr->master_crc)); *twoptr = CalcCRC(0, (onebyt *) bufPtr+8, MHsize-8); */ ArcfiCreate(filename); /* create SHK file */ return (archive); } /* * Return a pointer to a valid Master Header block * Anything that isn't set to a default value needs to be passed as a * parameter [ right now I can't remember why ]. */ onebyt * MakeMHblock(archive, total_records, master_eof) ListHdr *archive; fourbyt total_records; fourbyt master_eof; { static onebyt buf[MHsize]; /* must be static */ twobyt *twoptr; fourbyt *fourptr; int idx; static char *procName = "MakeMHblock"; for (idx = 0; idx < MHsize ; idx++) buf[idx] = '\0'; /* messy... should've used MHptr->thing here, but if it ain't broke... */ strncpy((char *) buf, (char *) MasterID, 6); BCopy((onebyt *) &total_records, (onebyt *) &buf[8], 4, TRUE); BCopy((onebyt *) &archive->MHptr->arc_create_when, (onebyt *) &buf[12], sizeof(Time), FALSE); BCopy((onebyt *) GetTime(), (onebyt *) &buf[20], sizeof(Time), FALSE); twoptr = (twobyt *) &buf[28]; /* master version */ *twoptr = OURMVERS; HiSwap((onebyt *) buf, 28, 29); /* correct byte ordering */ BCopy((onebyt *) &master_eof, (onebyt *) &buf[38], 4, TRUE); twoptr = (twobyt *) &buf[6]; *twoptr = CalcCRC(0, (onebyt *) &buf[8], MHsize-8); HiSwap((onebyt *) buf, 6, 7); return (buf); } /* * Add files to archive * * Read files from disk, adding them to the end of the archive as we go. * Update the master header block after all files have been added. */ static void Add(filename, namecount, names) char *filename; int namecount; char **names; { ListHdr *archive; int arcfd; file_info *FIArray[MAXARGS]; /* entries malloc()ed by EvalArgs */ int idx; onebyt *mptr; /* points to a MHblock suitable for writing */ long addSize; static char *procName = "Add"; /* expand wildcards/subdirectories, and get file info */ namecount = EvalArgs(namecount, names, FIArray, TRUE); if (!namecount) { if (verbose) printf("No files selected.\n"); Quit (0); } archive = CreateMaybe(filename); if ((arcfd = open(archive->arc_name, O_RDWR | O_BINARY)) < 0) Fatal("Unable to open archive", procName); if (lseek(arcfd, (off_t)archive->nextposn, S_ABS) < 0) /* seek to end */ Fatal("Unable to seek in archive", procName); for (idx = 0 ; idx < namecount; idx++) { #ifdef APW if (STOP()) Quit(1); /* check for OA-. */ #endif addSize = AddFile(arcfd, FIArray[idx]); archive->MHptr->master_eof += addSize; archive->nextposn += addSize; archive->MHptr->total_records++; } mptr = MakeMHblock(archive, archive->MHptr->total_records, archive->MHptr->master_eof); if (lseek(arcfd, (off_t) 0, S_ABS) < 0) Fatal("Unable to rewind archive for master header", procName); if (write(arcfd, mptr, MHsize) < MHsize) Fatal("Unable to update master header", procName); if (close(arcfd) < 0) Fatal("Unable to close archive", procName); if (domove) { if (verbose) printf("Deleteing files...\n"); for (idx = 0; idx < namecount; idx++) { if (verbose) { printf("%s...", FIArray[idx]->pathname); fflush(stdout); } if (unlink(FIArray[idx]->pathname) < 0) { if (verbose) printf("failed.\n"); } else { if (verbose) printf("done.\n"); } } } } /* * Main entry point for adding files. */ void NuAdd(filename, namecount, names, options) char *filename; int namecount; char **names; char *options; { char *optr; int idx; char type[5]; static char *procName = "NuAdd"; if (*options == 'm') domove = TRUE; if (*options == 'c') docreate = TRUE; /* change T subopt to convert FROM current system TO */ if (transfrom >= 0) { transto = transfrom; transfrom = -1; } Add(filename, namecount, names); /* do main processing */ } ate_when),8, FALSE); BCopy((onebyt *) bufPtr+12, (onebyt *) &(MHptr->arc_mod_when), 8, FALSE); fourptr = (fourbyt *) (&(MHptr->master_eof)); *fourptr = (fourbyt) MHsize; /* twoptr = (twobyt *) (&(MHptr->master_crc)); *twoptr = CalcCRC(0, (onebyt *) bufPtr+8, MHsize-8); */ ArcfiCreate(filename); /* create SHK fnulib/nublu.c 644 4170 1464 26054 5324373543 6641 /* * nublu.c - operations on Binary II archives * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "Compress" #endif #ifndef NO_BLU /***********************************/ #include "nudefs.h" #include #include #ifdef UNIX # include # include # include #endif #ifdef APW # include #endif #ifdef MSDOS # include # include # include # include # include #endif #include "nuview.h" /* file types for BLU */ #include "nuadd.h" /* need OptNum() */ #include "nupak.h" /* need unpak_SQU */ #include "nuetc.h" /* Binary II extraction routines are adapted from: */ /************************************************************************* ** ** ** Name : unblu ** ** Author : Marcel J.E. Mol ** ** Date : 10/05/88 (first release) ** ** Version : 2.20 ** ** Files : unblu.c Main source file ** ** ** ** ------------------------- Revision List ------------------------- ** ** Ver Date Name Remarks ** ** 1.00 10/05/88 Marcel Mol Raw copy of a basic program** ** 2.00 03/06/88 Marcel Mol Rewrite after blu info ** ** was send to the net ** ** 2.10 18/06/88 Marcel Mol Added filetype texts ** ** 2.20 23/09/88 Marcel Mol Show mod and creation time ** ** ** ************************************************************************/ /*char * copyright = "@(#) unblu.c 2.1 18/06/88 (c) M.J.E. Mol";*/ #define BUFSIZE 128 /* Blu block length */ /* global variables */ static char *progname; static char *blufile; static BOOLEAN extract = FALSE; /* extract (as opposed to just listing) */ /* * extract_file -- extract file fname from the archive fd. Fname * contains filelen bytes. * * If the first block has the .QQ magic numbers, go ahead and try to * unsqueeze it. Not the best way to go about it, but it works. */ static void extract_file(fd, fname, filelen) int fd; char *fname; /* 64 bytes */ long filelen; { int ofd; int n, i; int j, len; onebyt buf[BUFSIZE]; long full_len; int offset; static char *procName = "extract_file"; /*n = */ read(fd, buf, 70); /* read first few bytes */ lseek(fd, (off_t) -70, S_REL); /* back up */ if ((buf[0] == 0x76) && (buf[1] == 0xff)) { /* is it squeezed? */ i = 0; /* get the original file name */ while ((fname[i] = buf[4+i]) != '\0') i++; offset = 5+i; /* how far into file is end of filename? */ ConvFileName(fname); if (verbose) { printf("(as %s)...", fname); fflush(stdout); } } ConvFileName(fname); /* strip hi bits, adjust special chars, etc */ len = strlen(fname); /* (no longer used?) */ #ifdef FUBAR for (j = 0; j < len; j++) fname[j] &= 0x7f; /* clear hi bits */ #endif if (Exists(fname)) { if (interact) { if (verbose) printf("file exists, overwite"); else printf("%s exists, overwite", fname); if (!AskYesNo()) { /* return w/o overwriting */ full_len = ( (filelen / 128L) +1 ) * 128L; lseek(fd, (off_t) full_len, S_REL); return; } } if (verbose) { printf("overwriting..."); fflush(stdout); } if (unlink(fname) < 0) Fatal("Unable to remove existing file", procName); } if ((ofd = open(fname, O_BINARY|O_CREAT|O_WRONLY|O_TRUNC, (mode_t)0644))<0){ Fatal("Can't open destination file", "extract_file"); } if ((buf[0] == 0x76) && (buf[1] == 0xff)) { /* is it squeezed? */ if (verbose) { printf("unsqueezing..."); fflush(stdout); } full_len = ( (filelen / 128L) +1 ) * 128L; lseek(fd, (off_t) offset, S_REL); full_len -= offset; /* send unpak_SQU everything past fname */ unpak_SQU(fd, ofd, full_len); /* unsqueeze it */ } else { /* extract uncompressed */ lastseen = '\0'; /* used by crlf() translator */ while (filelen > 0L) { n = read(fd, buf, BUFSIZE); /* Read 128 bytes */ if (n != BUFSIZE) { fprintf(stderr, "Extract_BNY: %s file size broken\n", blufile); Quit(-1); } if (crlf(ofd, buf, (filelen >= BUFSIZE ? BUFSIZE : filelen)) != (filelen >= BUFSIZE ? BUFSIZE : filelen)) Fatal("Bad write", procName); filelen -= (long) BUFSIZE; } } close(ofd); /* Close destination file */ } /* * print_header -- print global information of the binary II file */ static void print_header(buf) onebyt *buf; { long disk_blocks; disk_blocks = buf[117] + (buf[118]<<8) + (buf[119]<<16) + (buf[120]<<24); printf("Listing %-40.40s ", blufile); printf("Blocks used: %-5ld", disk_blocks); printf("Files: %d\n", buf[127]+1); printf("\nFilename Type Blocks Modified "); printf("Created Length Subtype\n\n"); } /* * want -- return TRUE if name exists in array wantlist, * else return FALSE */ static BOOLEAN want(name, wantlist) char *name; char **wantlist; { while (*wantlist != NULL) { if (strcasecmp(name, *wantlist++) == 0) return (TRUE); } return (FALSE); } /* * process_file -- retrieve or print file information of file given * in buf */ static void process_file(fd, buf, count, wanted) int fd; onebyt *buf; int count; char **wanted; { int ftype, auxtype; int fnamelen; long filelen; char fname[64]; char outbuf[16]; /* temp for sprintf */ int nblocks, problocks; Time create_dt; Time mod_dt; #ifdef APW FileRec frec; #endif #ifdef UNIX struct stat st; #endif #ifdef MSDOS struct stat st; #endif /* +PORT+ */ /* int tf; * int dflags; */ static char *procName = "process_file"; /* Get file info */ ftype = buf[4]; /* File type */ auxtype = (int) buf[5] + ((int)buf[6] << 8); fnamelen = buf[23]; /* filename */ strncpy(fname, &buf[24], fnamelen); fname[fnamelen] = '\0'; /* dflags = buf[125]; /* Data flags */ /* tf = buf[127]; /* Number of files to follow */ filelen = (long) buf[20] + ((long) buf[21] << 8) + ((long) buf[22] << 16); /* calculate file len */ nblocks = (filelen + BUFSIZE-1) / BUFSIZE; /* #of BNY blocks */ problocks = buf[8] + ((int) buf[9] << 8); mod_dt.second = 0; mod_dt.minute = buf[12] & 0x3f; mod_dt.hour = buf[13] & 0x1f; mod_dt.day = (buf[10] & 0x1f) -1; mod_dt.month = (((buf[11] & 0x01) << 3) + (buf[10] >> 5)) -1; mod_dt.year = buf[11] >> 1; mod_dt.weekDay= 0; create_dt.second = 0; create_dt.minute = buf[16] & 0x3f; create_dt.hour = buf[17] & 0x1f; create_dt.day = (buf[14] & 0x1f) -1; create_dt.month = (((buf[15] & 0x01) << 3) + (buf[14] >> 5)) -1; create_dt.year = buf[15] >> 1; create_dt.weekDay= 0; if (!count || want(fname, wanted)) { if (!extract) { /* print file information ONLY */ printf("%-15.15s %-3.3s ", fname, FT[ftype]); printf("%6d ", problocks); printf("%-16.16s ", PrintDate(&mod_dt, TRUE)); printf("%-16.16s ", PrintDate(&create_dt, TRUE)); if (filelen < 0x100L) sprintf(outbuf, "$%.2lx", filelen); else if (filelen < 0x10000L) sprintf(outbuf, "$%.4lx", filelen); else sprintf(outbuf, "$%.6lx", filelen); printf("%7s ", outbuf); printf("$%.4x\n", auxtype); /* if (dflags == 0) * printf("stored"); * else { * if (dflags & 128) { * printf("squeezed"); * } * if (dflags & 64) { * printf("encrypted"); * } * if (dflags & 1) * printf("packed"); * } * putchar('\n'); */ if (ftype != 15) { /* If not a directory */ lseek(fd, (off_t) BUFSIZE*nblocks, S_REL); /*Seek to next file*/ } } else { /* extract is TRUE */ if (verbose) { printf("Extracting %s...", fname); fflush(stdout); } #ifdef UNIX if (ftype != 15) extract_file(fd, fname, filelen); /* note dates etc not set */ else { /* if no directory exists, then make one */ if (stat(fname, &st) < 0) if (errno == ENOENT) { sprintf(tmpNameBuf, "mkdir %s", fname); if (system(tmpNameBuf) != 0) /* call UNIX mkdir */ Fatal("Unable to create subdir", procName); } else { Fatal("Unable to create dir", procName); } } #else # ifdef APW /* create file/directory , with appropriate type/auxtype stuff */ c2pstr(fname); frec.pathname = fname; frec.fAccess = 0x00e3; /* unlocked */ frec.fileType = ftype; frec.auxType = (unsigned long) auxtype; frec.storageType = (int) buf[7]; frec.createDate = 0x0000; /* set later */ frec.createTime = 0x0000; CREATE( &frec ); ToolErrChk(); p2cstr(fname); extract_file(fd, fname, filelen); /* set file attributes */ c2pstr(fname); frec.fAccess = (word) buf[3]; frec.modDate = (word) buf[10] + ((word)buf[11] << 8); frec.modTime = (word) buf[12] + ((word)buf[13] << 8); frec.createDate = (word) buf[14] + ((word)buf[15] << 8); frec.createTime = (word) buf[16] + ((word)buf[17] << 8); SET_FILE_INFO( &frec ); ToolErrChk(); p2cstr(fname); # else if (ftype != 15) extract_file(fd, fname, filelen); else /* +PORT+ */ printf("[ need [other] subdir create for UnBNY ]\n"); # endif /* APW */ #endif /* UNIX */ if (verbose) printf("done.\n"); } } else if (ftype != 15) { /* This file not wanted; if not a directory */ lseek(fd, (off_t) BUFSIZE*nblocks, S_REL); /* Seek to next file */ } } /* * unblu -- process a binary II file fd, and process the filenames * listed in wanted. If wanted is \0, all files are processed. */ static void unblu(fd, count, wanted) int fd; int count; char **wanted; { onebyt buf[BUFSIZE]; int firstblock = 1; /* First block needs special processing */ int tofollow = 1; /* Files to follow in the archive */ int n; while (tofollow && ((n = read(fd, buf, BUFSIZE)) > 0)) { /* If there is a header block */ if (n != BUFSIZE) { fprintf(stderr, "UnBNY: %s file size is broken\n", blufile); Quit(-1); } if ((buf[0] != 10) || (buf[1] != 71) || (buf[2] != 76) || (buf[18] != 2)) { fprintf(stderr, "UnBNY: %s not a Binary II file or bad record\n", blufile); Quit(-1); } tofollow = buf[127]; /* How many files to follow */ if (firstblock && !extract) { print_header(buf); } firstblock = 0; process_file(fd, buf, count, wanted); /* process the file for it */ } if (firstblock && (n < 0)) /* length < 128 */ fprintf(stderr, "UnBNY: Not a Binary II file"); } /* * Main entry point from CShrink */ void NuBNY(filename, argc, argv, options) char *filename; int argc; char **argv; char *options; { int bfd; /* File descriptor for blu file */ char *optr; /* process X subopt ourselves */ if (INDEX(options+1, 'x')) extract = TRUE; else extract = FALSE; blufile = filename; /* Make it global */ if ((bfd = open(filename, O_RDONLY | O_BINARY)) < 0) Fatal("Unable to open Binary II archive", "NuBNY"); unblu(bfd, argc, argv); /* Process wanted files */ close(bfd); Quit(0); } #endif /*NO_BLU*/ /***********************************/ en); else if (filelen < 0x10000L) sprintf(outbuf, "$%.4lx", filelen); else sprintf(outbuf, "$%.6lx", filelen); printf("%7s ", outbuf); printf("$%.4x\n", auxtype); /* if (dflags == 0) * printf("stored"); * else { * if (dflags & 128) { * printf("squeezed"); * } * if (dflags & 64) { * printf("encrypted"); * } * if (dflags & 1) * printf("packed"); * } * putchar('\n'); */ if (ftype != 15) nulib/nucomp.c 644 4170 1464 74216 5315527215 7015 /* * nucomp.c - code to perform UNIX style LZW compression * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ /* * This is the main compression code from compress v4.3. Modifications * have been made to integrate it with NuLib (primarily in that it no longer * uses stdin/stdout), but it's functionally the same. */ #ifdef APW segment "Compress" #endif #include "nudefs.h" #include "nupak.h" #define MAIN /* cause nucomp.h to alloc global vars */ /*@H************************ < COMPRESS API > **************************** * * * compress : compapi.c * * * * port by : Donald J. Gloistein * * * * Source, Documentation, Object Code: * * released to Public Domain. This code is based on code as documented * * below in release notes. * * * *--------------------------- Module Description --------------------------* * Contains source code for modified Lempel-Ziv method (LZW) compression * * and decompression. * * * * This code module can be maintained to keep current on releases on the * * Unix system. The command shell and dos modules can remain the same. * * * *--------------------------- Implementation Notes --------------------------* * * * compiled with : compress.h compress.fns compress.c * * linked with : compress.obj compusi.obj * * * * problems: * * * * * * CAUTION: Uses a number of defines for access and speed. If you change * * anything, make sure about side effects. * * * * Compression: * * Algorithm: use open addressing double hashing (no chaining) on the * * prefix code / next character combination. We do a variant of Knuth's * * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime * * secondary probe. Here, the modular division first probe is gives way * * to a faster exclusive-or manipulation. * * Also block compression with an adaptive reset was used in original code, * * whereby the code table is cleared when the compression ration decreases * * but after the table fills. This was removed from this edition. The table * * is re-sized at this point when it is filled , and a special CLEAR code is * * generated for the decompressor. This results in some size difference from * * straight version 4.0 joe Release. But it is fully compatible in both v4.0 * * and v4.01 * * * * Decompression: * * This routine adapts to the codes in the file building the "string" table * * on-the-fly; requiring no table to be stored in the compressed file. The * * tables used herein are shared with those of the compress() routine. * * * * Initials ---- Name --------------------------------- * * DjG Donald J. Gloistein, current port to MsDos 16 bit * * Plus many others, see rev.hst file for full list * * LvR Lyle V. Rains, many thanks for improved implementation * * of the compression and decompression routines. * *************************************************************************@H*/ #include #define assert(x) #include "nucomp.h" /* contains the rest of the include file declarations */ FILE *nustdin, *nustdout; /* NuLib: use these instead of stdin/stdout */ long nubytes_read, nucomp_thread_eof; /* NuLib: used in nextcode (decomp) */ /* NuLib: pulled this out of nextcode() so we can initialize it every time */ static int prevbits = 0; /* NuLib: pulled out of putcode() */ static int oldbits = 0; static int offset; static long int in_count ; /* length of input */ static long int bytes_out; /* length of compressed output */ static long int max_bytes_out; /* NuLib: max #of bytes to output */ static CODE prefxcode, nextfree; static CODE highcode; static CODE maxcode; static HASH hashsize; static int bits; /* * The following two parameter tables are the hash table sizes and * maximum code values for various code bit-lengths. The requirements * are that Hashsize[n] must be a prime number and Maxcode[n] must be less * than Maxhash[n]. Table occupancy factor is (Maxcode - 256)/Maxhash. * Note: I am using a lower Maxcode for 16-bit codes in order to * keep the hash table size less than 64k entries. */ CONST HASH hs[] = { 0x13FF, /* 12-bit codes, 75% occupancy */ 0x26C3, /* 13-bit codes, 80% occupancy */ 0x4A1D, /* 14-bit codes, 85% occupancy */ 0x8D0D, /* 15-bit codes, 90% occupancy */ 0xFFD9 /* 16-bit codes, 94% occupancy, 6% of code values unused */ }; #define Hashsize(maxb) (hs[(maxb) -MINBITS]) CONST CODE mc[] = { 0x0FFF, /* 12-bit codes */ 0x1FFF, /* 13-bit codes */ 0x3FFF, /* 14-bit codes */ 0x7FFF, /* 15-bit codes */ 0xEFFF /* 16-bit codes, 6% of code values unused */ }; #define Maxcode(maxb) (mc[(maxb) -MINBITS]) #define allocx(type,ptr,size) \ (((ptr) = (type FAR *) emalloc((unsigned int)(size),sizeof(type))) == NULLPTR(type) \ ? NOMEM : OK \ ) #define free_array(type,ptr,offset) \ if (ptr != NULLPTR(type)) { \ efree((ALLOCTYPE FAR *)((ptr) + (offset))); \ (ptr) = NULLPTR(type); \ } /* * Macro to allocate new memory to a pointer with an offset value. */ #define alloc_array(type, ptr, size, offset) \ ( allocx(type, ptr, (size) - (offset)) != OK \ ? NOMEM \ : (((ptr) -= (offset)), OK) \ ) static char FAR *sfx = NULLPTR(char) ; #define suffix(code) sfx[code] #ifdef SPLIT_PFX static CODE FAR *pfx[2] = {NULLPTR(CODE), NULLPTR(CODE)}; #else static CODE FAR *pfx = NULLPTR(CODE); #endif #ifdef SPLIT_HT static CODE FAR *ht[2] = {NULLPTR(CODE),NULLPTR(CODE)}; #else static CODE FAR *ht = NULLPTR(CODE); #endif int alloc_tables(maxcode, hashsize) CODE maxcode; HASH hashsize; { static CODE oldmaxcode = 0; static HASH oldhashsize = 0; if (hashsize > oldhashsize) { #ifdef SPLIT_HT free_array(CODE,ht[1], 0); free_array(CODE,ht[0], 0); #else free_array(CODE,ht, 0); #endif oldhashsize = 0; } if (maxcode > oldmaxcode) { #ifdef SPLIT_PFX free_array(CODE,pfx[1], 128); free_array(CODE,pfx[0], 128); #else free_array(CODE,pfx, 256); #endif free_array(char,sfx, 256); if ( alloc_array(char, sfx, maxcode + 1, 256) #ifdef SPLIT_PFX || alloc_array(CODE, pfx[0], (maxcode + 1) / 2, 128) || alloc_array(CODE, pfx[1], (maxcode + 1) / 2, 128) #else || alloc_array(CODE, pfx, (maxcode + 1), 256) #endif ) { oldmaxcode = 0; exit_stat = NOMEM; return(NOMEM); } oldmaxcode = maxcode; } if (hashsize > oldhashsize) { if ( #ifdef SPLIT_HT alloc_array(CODE, ht[0], (hashsize / 2) + 1, 0) || alloc_array(CODE, ht[1], hashsize / 2, 0) #else alloc_array(CODE, ht, hashsize, 0) #endif ) { oldhashsize = 0; exit_stat = NOMEM; return(NOMEM); } oldhashsize = hashsize; } return (OK); } # ifdef SPLIT_PFX /* * We have to split pfx[] table in half, * because it's potentially larger than 64k bytes. */ # define prefix(code) (pfx[(code) & 1][(code) >> 1]) # else /* * Then pfx[] can't be larger than 64k bytes, * or we don't care if it is, so we don't split. */ # define prefix(code) (pfx[code]) # endif /* The initializing of the tables can be done quicker with memset() */ /* but this way is portable through out the memory models. */ /* If you use Microsoft halloc() to allocate the arrays, then */ /* include the pragma #pragma function(memset) and make sure that */ /* the length of the memory block is not greater than 64K. */ /* This also means that you MUST compile in a model that makes the */ /* default pointers to be far pointers (compact or large models). */ /* See the file COMPUSI.DOS to modify function emalloc(). */ # ifdef SPLIT_HT /* * We have to split ht[] hash table in half, * because it's potentially larger than 64k bytes. */ # define probe(hash) (ht[(hash) & 1][(hash) >> 1]) # define init_tables() \ { \ hash = hashsize >> 1; \ ht[0][hash] = 0; \ while (hash--) ht[0][hash] = ht[1][hash] = 0; \ highcode = ~(~(CODE)0 << (bits = INITBITS)); \ nextfree = (block_compress ? FIRSTFREE : 256); \ } # else /* * Then ht[] can't be larger than 64k bytes, * or we don't care if it is, so we don't split. */ # define probe(hash) (ht[hash]) # define init_tables() \ { \ hash = hashsize; \ while (hash--) ht[hash] = 0; \ highcode = ~(~(CODE)0 << (bits = INITBITS)); \ nextfree = (block_compress ? FIRSTFREE : 256); \ } # endif #ifdef COMP40 /* table clear for block compress */ /* this is for adaptive reset present in version 4.0 joe release */ /* DjG, sets it up and returns TRUE to compress and FALSE to not compress */ int cl_block () { register long int rat; checkpoint = in_count + CHECK_GAP; #ifndef NDEBUG if ( debug ) { fprintf ( stderr, "count: %ld, ratio: ", in_count ); prratio ( stderr, in_count, bytes_out ); fprintf ( stderr, "\n"); } #endif if(in_count > 0x007fffff) { /* shift will overflow */ rat = bytes_out >> 8; if(rat == 0) /* Don't divide by zero */ rat = 0x7fffffff; else rat = in_count / rat; } else rat = (in_count << 8) / bytes_out; /* 8 fractional bits */ if ( rat > ratio ){ ratio = rat; return FALSE; } else { ratio = 0; #ifndef NDEBUG if(debug) fprintf ( stderr, "clear\n" ); #endif return TRUE; /* clear the table */ } /*NOTREACHED*/ /* return FALSE; /* don't clear the table */ } #endif /*COMP40*/ /* * compress stdin to stdout <-- nope * NuLib: compress thread_eof bytes from srcfd, writing to dstfd * Sets up a few things and then calls u_compress. */ int u_compress(srcfd, dstfd, thread_eof) int srcfd, dstfd; long thread_eof; { int src2, dst2; long srcposn, dstposn; static char *procName = "u_compress"; if ((srcposn = (long) lseek(srcfd, (off_t) 0, S_REL)) < 0) Fatal("Bad posn lseek(1)", procName); if ((dstposn = (long) lseek(dstfd, (off_t) 0, S_REL)) < 0) Fatal("Bad posn lseek(2)", procName); src2 = dup(srcfd); dst2 = dup(dstfd); /* NuLib: open new stdin/stdout, and seek */ if ((nustdin = fdopen(src2, FREAD_STR)) == NULL) Fatal("can't fdopen() nustdin", procName); if ((nustdout = fdopen(dst2, FWRITE_STR)) == NULL) Fatal("can't fdopen() nustdout", procName); setvbuf(nustdin,xbuf,_IOFBF,XBUFSIZE); /* make the buffers larger */ setvbuf(nustdout,zbuf,_IOFBF,ZBUFSIZE); /* (note setvbuf is a macro) */ if (fseek(nustdin, (off_t)srcposn, S_ABS) < 0) /* seek may not be needed */ Fatal("Bad stream posn lseek(1)", procName); if (fseek(nustdout, (off_t)dstposn, S_ABS) < 0) Fatal("Bad stream posn lseek(2)", procName); oldbits = 0; /* init for putcode() */ compress(thread_eof); check_error(); fclose(nustdin); /* note this closes the duped fd */ fclose(nustdout); return (exit_stat); } void compress(thread_eof) long thread_eof; { int c,adjbits; register HASH hash; register CODE code; HASH hashf[256]; max_bytes_out = thread_eof; /* NuLib: don't exceed original size */ maxcode = Maxcode(maxbits); hashsize = Hashsize(maxbits); #ifdef COMP40 /* Only needed for adaptive reset */ checkpoint = CHECK_GAP; ratio = 0; #endif adjbits = maxbits -10; for (c = 256; --c >= 0; ){ hashf[c] = ((( c &0x7) << 7) ^ c) << adjbits; } exit_stat = OK; if (alloc_tables(maxcode, hashsize)) /* exit_stat already set */ return; init_tables(); /* if not zcat or filter (NuLib: never happens) */ if(is_list && !zcat_flg) { /* Open output file */ if (freopen(ofname, WRITE_FILE_TYPE, nustdout) == NULL) { exit_stat = NOTOPENED; return; } if (!quiet) fprintf(stderr, "%s: ",ifname); setvbuf(nustdout,zbuf,_IOFBF,ZBUFSIZE); } /* * Check the input stream for previously seen strings. We keep * adding characters to the previously seen prefix string until we * get a character which forms a new (unseen) string. We then send * the code for the previously seen prefix string, and add the new * string to our tables. The check for previous strings is done by * hashing. If the code for the hash value is unused, then we have * a new string. If the code is used, we check to see if the prefix * and suffix values match the current input; if so, we have found * a previously seen string. Otherwise, we have a hash collision, * and we try secondary hash probes until we either find the current * string, or we find an unused entry (which indicates a new string). */ if (!nomagic) { putc(magic_header[0], nustdout); /* was putchar() */ putc(magic_header[1], nustdout); /* was putchar() */ putc((char)(maxbits | block_compress), nustdout); /*was putchar*/ if(ferror(nustdout)){ /* check it on entry */ exit_stat = WRITEERR; return; } bytes_out = 3L; /* includes 3-byte header mojo */ } else bytes_out = 0L; /* no 3-byte header mojo */ in_count = 1L; offset = 0; if ((c = getc(nustdin)) == EOF) { /* NuLib: was getchar() */ exit_stat = ferror(nustdin) ? READERR : OK; return; } prefxcode = (CODE)c; while ((c = getc(nustdin)) != EOF) { /* NuLib: was getchar() */ in_count++; /* NuLib : May not be compressing entire file, so can't rely on EOF for end */ if (in_count > thread_eof) break; hash = prefxcode ^ hashf[c]; /* I need to check that my hash value is within range * because my 16-bit hash table is smaller than 64k. */ if (hash >= hashsize) hash -= hashsize; if ((code = probe(hash)) != UNUSED) { if (suffix(code) != (char)c || prefix(code) != prefxcode) { /* hashdelta is subtracted from hash on each iteration of * the following hash table search loop. I compute it once * here to remove it from the loop. */ HASH hashdelta = (0x120 - c) << (adjbits); do { /* rehash and keep looking */ assert(code >= FIRSTFREE && code <= maxcode); if (hash >= hashdelta) hash -= hashdelta; else hash += (hashsize - hashdelta); assert(hash < hashsize); if ((code = probe(hash)) == UNUSED) goto newcode; } while (suffix(code) != (char)c || prefix(code) != prefxcode); } prefxcode = code; } else { newcode: { putcode(prefxcode, bits); code = nextfree; assert(hash < hashsize); assert(code >= FIRSTFREE); assert(code <= maxcode + 1); if (code <= maxcode) { probe(hash) = code; prefix(code) = prefxcode; suffix(code) = (char)c; if (code > highcode) { highcode += code; ++bits; } nextfree = code + 1; } #ifdef COMP40 else if (in_count >= checkpoint && block_compress ) { if (cl_block()){ #else else if (block_compress){ #endif putcode((CODE)c, bits); putcode((CODE)CLEAR,bits); init_tables(); if ((c = getc(nustdin)) == EOF) /* NuLib: was getchar*/ break; in_count++; #ifdef COMP40 } #endif } prefxcode = (CODE)c; } } } putcode(prefxcode, bits); putcode((CODE)CLEAR, 0); if (ferror(nustdout)){ /* check it on exit */ exit_stat = WRITEERR; return; } /* * Print out stats on stderr */ if(zcat_flg == 0 && !quiet) { #ifndef NDEBUG fprintf( stderr, "%ld chars in, (%ld bytes) out, compression factor: ", in_count, bytes_out ); prratio( stderr, in_count, bytes_out ); fprintf( stderr, "\n"); fprintf( stderr, "\tCompression as in compact: " ); prratio( stderr, in_count-bytes_out, in_count ); fprintf( stderr, "\n"); fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n", prefxcode - 1, bits ); #else fprintf( stderr, "Compression: " ); prratio( stderr, in_count-bytes_out, in_count ); #endif /* NDEBUG */ } if(bytes_out > in_count) /* if no savings */ exit_stat = NOSAVING; packedSize = bytes_out; /* NuLib : return packed size in global */ return ; } CONST UCHAR rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; void putcode(code,bits) CODE code; register int bits; { static UCHAR outbuf[MAXBITS]; register UCHAR *buf; register int shift; register int ok_to_write; /* NuLib (kludge... sorry) */ ok_to_write = (exit_stat != NOSAVING); if (bits != oldbits) { if (bits == 0) { /* bits == 0 means EOF, write the rest of the buffer. */ if (offset > 0) { int x = ((offset+7) >> 3); /* NuLib */ if ((bytes_out + x) > max_bytes_out) { /* NuLib */ /* compression failed. There's no clean way of bailing out /* (could use setjmp/longjmp, but that may not be supported /* on all systems), so just don't write anything. */ exit_stat = NOSAVING; ok_to_write = FALSE; } else { /* fwrite(outbuf,1,(offset +7) >> 3, nustdout);*/ fwrite(outbuf,1, x, nustdout); } /* bytes_out += ((offset +7) >> 3);*/ bytes_out += x; } offset = 0; oldbits = 0; fflush(nustdout); return; } else { /* Change the code size. We must write the whole buffer, * because the expand side won't discover the size change * until after it has read a buffer full. */ if (offset > 0) { if (ok_to_write) fwrite(outbuf, 1, oldbits, nustdout); bytes_out += oldbits; offset = 0; } oldbits = bits; #ifndef NDEBUG if ( debug ) fprintf( stderr, "\nChange to %d bits\n", bits ); #endif /* !NDEBUG */ } } /* Get to the first byte. */ buf = outbuf + ((shift = offset) >> 3); if ((shift &= 7) != 0) { *(buf) |= (*buf & rmask[shift]) | (UCHAR)(code << shift); *(++buf) = (UCHAR)(code >> (8 - shift)); if (bits + shift > 16) *(++buf) = (UCHAR)(code >> (16 - shift)); } else { /* Special case for fast execution */ *(buf) = (UCHAR)code; *(++buf) = (UCHAR)(code >> 8); } if ((offset += bits) == (bits << 3)) { bytes_out += bits; if (ok_to_write) fwrite(outbuf,1,bits,nustdout); offset = 0; } return; } int nextcode(codeptr) CODE *codeptr; /* Get the next code from input and put it in *codeptr. * Return (TRUE) on success, or return (FALSE) on end-of-file. * Adapted from COMPRESS V4.0. */ { register CODE code; static int size; static UCHAR inbuf[MAXBITS]; register int shift; UCHAR *bp; /* If the next entry is a different bit-size than the preceeding one * then we must adjust the size and scrap the old buffer. */ if (prevbits != bits) { prevbits = bits; size = 0; } /* If we can't read another code from the buffer, then refill it. */ if (size - (shift = offset) < bits) { static int bytesize; /* NuLib: sigh */ /* Read more input and convert size from # of bytes to # of bits */ /* NuLib: stop after comp_thread_eof bytes */ if (nubytes_read >= nucomp_thread_eof) return(NO); /* NuLib: replace old fread command with... */ /* if ((size = fread(inbuf, 1, bits, nustdin) << 3) <= 0 || ferror(nustdin)) return(NO); */ bytesize = fread(inbuf, 1, bits, nustdin); if (nubytes_read + bits > nucomp_thread_eof) { bytesize = nucomp_thread_eof - nubytes_read; } size = bytesize << 3; if (size <= 0 || ferror(nustdin)) return (NO); /* NuLib: increment nubytes_read */ nubytes_read += (long) bytesize; offset = shift = 0; } /* Get to the first byte. */ bp = inbuf + (shift >> 3); /* Get first part (low order bits) */ code = (*bp++ >> (shift &= 7)); /* high order bits. */ code |= *bp++ << (shift = 8 - shift); if ((shift += 8) < bits) code |= *bp << shift; *codeptr = code & highcode; offset += bits; return (TRUE); } /* * NuLib: uncompress comp_thread_eof bytes from srcfd, writing to dstfd * Sets up a few things and then calls compress. */ int u_decompress(srcfd, dstfd, comp_thread_eof) int srcfd, dstfd; long comp_thread_eof; { int src2, dst2; long srcposn, dstposn; static char *procName = "u_decompress"; if ((srcposn = (long)lseek(srcfd, (off_t) 0, S_REL)) < 0) Fatal("Bad posn lseek(1)", procName); if ((dstposn = (long)lseek(dstfd, (off_t) 0, S_REL)) < 0) Fatal("Bad posn lseek(2)", procName); src2 = dup(srcfd); dst2 = dup(dstfd); /* NuLib: open new stdin/stdout, and seek */ if ((nustdin = fdopen(src2, FREAD_STR)) == NULL) Fatal("can't fdopen() nustdin", procName); if ((nustdout = fdopen(dst2, FWRITE_STR)) == NULL) Fatal("can't fdopen() nustdout", procName); setvbuf(nustdin,zbuf,_IOFBF,ZBUFSIZE); /* make the buffers larger */ setvbuf(nustdout,xbuf,_IOFBF,XBUFSIZE); /* (note order diff from comp) */ if (fseek(nustdin, (off_t)srcposn, S_ABS) < 0) /* seek may not be needed */ Fatal("Bad stream posn lseek(1)", procName); if (fseek(nustdout, (off_t)dstposn, S_ABS) < 0) Fatal("Bad stream posn lseek(2)", procName); /* Check the magic number */ if (!nomagic) { if ((getc(nustdin)!=(magic_header[0] & 0xFF)) /* NuLib: was getchar*/ || (getc(nustdin)!=(magic_header[1] & 0xFF))) {/* NuLib: was getchar*/ fprintf(stderr, "decompress: not in compressed format\n"); return(-1); /* NuLib: was exit(ERROR) */ } maxbits = getc(nustdin); /* set -b from file (NuLib: was getchar) */ block_compress = maxbits & BLOCK_MASK; maxbits &= BIT_MASK; if(maxbits > MAXBITS) { fprintf(stderr, "decompress: compressed with %d bits, can only handle %d bits\n", maxbits, MAXBITS); return(-1); /* NuLib: was exit(ERROR) */ } nubytes_read = 3L; } else { nubytes_read = 0L; } nucomp_thread_eof = comp_thread_eof; /* printf("src file posn = %ld\n", ftell(nustdin));*/ prevbits = 0; /* init for nextcode() */ decompress(); check_error(); fclose(nustdin); /* note this closes the duped fd */ fclose(nustdout); return (exit_stat); } void decompress() { register int i; register CODE code; char sufxchar; CODE savecode; FLAG fulltable, cleartable; static char token[MAXTOKLEN]; /* String buffer to build token */ exit_stat = OK; if (alloc_tables(maxcode = ~(~(CODE)0 << maxbits),0)) /* exit_stat already set */ return; /* if not zcat or filter (NuLib: never) */ if(is_list && !zcat_flg) { /* Open output file */ if (freopen(ofname, WRITE_FILE_TYPE, nustdout) == NULL) { exit_stat = NOTOPENED; return; } if (!quiet) fprintf(stderr, "%s: ",ifname); setvbuf(nustdout,xbuf,_IOFBF,XBUFSIZE); } cleartable = TRUE; savecode = CLEAR; offset = 0; do { if ((code = savecode) == CLEAR && cleartable) { highcode = ~(~(CODE)0 << (bits = INITBITS)); fulltable = FALSE; nextfree = (cleartable = block_compress) == FALSE ? 256 : FIRSTFREE; if (!nextcode(&prefxcode)) break; putc((sufxchar = (char)prefxcode), nustdout); continue; } i = 0; if (code >= nextfree && !fulltable) { if (code != nextfree){ exit_stat = CODEBAD; /* fprintf(stderr, "Bad code; nubytes_read = %ld\n", nubytes_read); */ /* CDEBUG */ return ; /* Non-existant code */ } /* Special case for sequence KwKwK (see text of article) */ code = prefxcode; token[i++] = sufxchar; } /* Build the token string in reverse order by chasing down through * successive prefix tokens of the current token. Then output it. */ while (code >= 256) { #ifndef NDEBUG /* These are checks to ease paranoia. Prefix codes must decrease * monotonically, otherwise we must have corrupt tables. We can * also check that we haven't overrun the token buffer. */ if (code <= prefix(code)){ exit_stat= TABLEBAD; return; } if (i >= MAXTOKLEN){ exit_stat = TOKTOOBIG; return; } #endif token[i++] = suffix(code); code = prefix(code); } putc(sufxchar = (char)code, nustdout); while (--i >= 0) putc(token[i], nustdout); if (ferror(nustdout)) { exit_stat = WRITEERR; return; } /* If table isn't full, add new token code to the table with * codeprefix and codesuffix, and remember current code. */ if (!fulltable) { code = nextfree; assert(256 <= code && code <= maxcode); prefix(code) = prefxcode; suffix(code) = sufxchar; prefxcode = savecode; if (code++ == highcode) { if (highcode >= maxcode) { fulltable = TRUE; --code; } else { ++bits; highcode += code; /* nextfree == highcode + 1 */ } } nextfree = code; } } while (nextcode(&savecode)); exit_stat = (ferror(nustdin))? READERR : OK; return ; } /* * These are routines pulled out of "compress.c" from compress v4.3. */ void prratio(stream, num, den) FILE *stream; long int num, den; { register int q; /* Doesn't need to be long */ if(num > 214748L) { /* 2147483647/10000 */ q = (int) (num / (den / 10000L)); } else { q = (int) (10000L * num / den); /* Long calculations, though */ } if (q < 0) { putc('-', stream); q = -q; } fprintf(stream, "%d.%02d%%", q / 100, q % 100); } /* * Check exit status from compress() and decompress() * * exit_stat is a global var. Either returns something interesting or * bails out completely. */ int check_error() /* returning OK continues with processing next file */ { prog_name = prgName; /* NuLib: set prog_name to "nulib" */ switch(exit_stat) { case OK: return (OK); case NOMEM: if (do_decomp) fprintf(stderr,"%s: not enough memory to decompress '%s'.\n", prog_name, ifname); else fprintf(stderr,"%s: not enough memory to compress '%s'.\n", prog_name, ifname); return(OK); case SIGNAL_ERROR: fprintf(stderr,"%s: error setting signal interupt.\n",prog_name); exit(ERROR); break; case READERR: fprintf(stderr,"%s: read error on input '%s'.\n", prog_name, ifname); break; case WRITEERR: fprintf(stderr,"%s: write error on output '%s'.\n", prog_name, ofname); break; case TOKTOOBIG: fprintf(stderr,"%s: token too long in '%s'.\n", prog_name, ifname); break; case INFILEBAD: fprintf(stderr, "%s: '%s' in unknown compressed format.\n", prog_name, ifname); break; case CODEBAD: fprintf(stderr,"%s: file token bad in '%s'.\n", prog_name,ifname); break; case TABLEBAD: fprintf(stderr,"%s: internal error -- tables corrupted.\n", prog_name); break; case NOTOPENED: fprintf(stderr,"%s: could not open output file %s\n",prog_name,ofname); exit(ERROR); break; case NOSAVING: if (force) exit_stat = OK; return (OK); default: fprintf(stderr,"%s: internal error -- illegal return value = %d.\n", prog_name,exit_stat); } if (!zcat_flg && !keep_error){ fclose(nustdout); /* won't get here without an error */ unlink ( ofname ); } exit(exit_stat); return(ERROR); } /* * These are routines from "compusi.c" */ void version() { #ifdef XENIX #ifndef NDEBUG fprintf(stderr, "%s\nOptions: Xenix %s MAXBITS = %d\n", rcs_ident, "DEBUG",MAXBITS); #else fprintf(stderr, "%s\nOptions: Xenix MAXBITS = %d\n", rcs_ident,MAXBITS); #endif #else #ifndef NDEBUG fprintf(stderr, "%s\nOptions: Unix %s MAXBITS = %d\n", rcs_ident, "DEBUG",MAXBITS); #else fprintf(stderr, "%s\nOptions: Unix MAXBITS = %d\n", rcs_ident,MAXBITS); #endif #endif } ALLOCTYPE FAR * emalloc(x,y) unsigned int x; int y; { ALLOCTYPE FAR *p; p = (ALLOCTYPE FAR *)ALLOCATE(x,y); return(p); } void efree(ptr) ALLOCTYPE FAR *ptr; { FREEIT(ptr); } exit_stat= TABLEBAD; return; } if (i >= MAXTOKLEN){ exit_stat = TOKTOOBIG; return; } #endif token[i++] = suffix(code); code = prefix(code); } putc(sufxchar = (char)code, nustdout); while (--i >= 0) putc(token[i], nustdout); if (ferror(nustdout)) { exit_stat = WRInulib/nuetc.c 644 4170 1464 40365 5310761305 6623 /* * nuetc.c - extra stuff; mostly system-dependent subroutines. * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "NuMain" #endif #include "nudefs.h" #include #include #include /* errno declarations */ #include /* for tolower(), isupper() */ #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 #include "nuetc.h" /* note "nuread.h" is not included... none of the routines here assume */ /* any knowledge of NuFX archives. */ #ifndef MSDOS extern char *malloc(); /* all systems ... except DOS : RBH */ #endif /* +PORT+ */ extern void free(); extern char *getenv(); #ifdef APW extern Time ReadTimeHex(); /* should be TimeRec, from misctool.h */ #endif /* This is a generally available TEMPORARY filename buffer */ char tmpNameBuf[MAXFILENAME]; /******************** general misc routines ********************/ /* * Fatal error handler */ void Fatal(deathstr, procName) char *deathstr, *procName; { fflush(stdout); fprintf(stderr, "\n%s: fatal error: %s\n--- ", prgName, deathstr); perror(procName); Quit (-1); } /* * Quit can be used to perform cleanup operations before exiting. */ void Quit(val) int val; { exit(val); } /* * Safe malloc()... checks return value */ char * Malloc(size) int size; { char *ptr = (char *) malloc(size); if (ptr != (char *) NULL) { return(ptr); } else { /* 8910.31 - RBH: report byte size that failed */ printf("Malloc: memory alloc error [%u : bytes]\n", size); #ifdef MSDOS /* (this doesn't work under Turbo C - MSC only) */ printf("(Largest Available Block: %u)\n", _memmax()); #endif Quit (-1); /*NOTREACHED*/ } } /******************** UNIX compatibility routines ********************/ #ifdef UNIX /* * Convert expanded date to a number of seconds for UNIX systems. * * Remember that *atime follows the NuFX docs for time values, which * don't necessarily match those in UNIX manual pages. * Adapted from _Advanced UNIX Programming_ by Marc J. Rochkind. * * Returns 0 if date/time is invalid. */ long timecvt(atime) Time *atime; { long tm; int days; BOOLEAN isleapyear; int tzone; char *getenv(), *tz; if ((tz = getenv("TZ")) == NULL) tzone = 8; /* pacific std time */ else tzone = atoi(&tz[3]); isleapyear = (atime->year != 0) && (atime->year%4 == 0); /* 2000 isn't */ if (atime->year < 70) atime->year += 100; /* years after 2000 */ days = (atime->year - 70) * 365L; days += ((atime->year - 69L) / 4); /* previous years' leap days */ switch (atime->month +1) { /* month is 0-11 */ case 12: days += 30; /* Nov */ case 11: days += 31; /* Oct */ case 10: days += 30; /* Sep */ case 9: days += 31; /* Aug */ case 8: days += 31; /* Jul */ case 7: days += 30; /* Jun */ case 6: days += 31; /* May */ case 5: days += 30; /* Apr */ case 4: days += 31; /* Mar */ case 3: days += (isleapyear ? 29 : 28); /* Feb */ case 2: days += 31; /* Jan */ case 1: break; default: /*printf("Invalid month\n");*/ return (0L); } if (atime->day > 31) { /*printf("Invalid day\n");*/ return (0L); } tm = (days + atime->day) * 24L * 60L * 60L; if (atime->hour > 23) { /*printf("Invalid hour\n");*/ return (0L); } atime->hour += tzone; /* correct for time zone */ tm += atime->hour * 60L * 60L; if (atime->minute > 59) { /*printf("Invalid minute\n");*/ return (0L); } tm += atime->minute * 60L; if (atime->second > 59) { /*printf("Invalid second\n");*/ return (0L); } tm += atime->second; if (localtime(&tm)->tm_isdst) /* was that day in dst? */ tm -= 60L * 60L; /* adjust for daylight savings */ return (tm); } #endif /* UNIX */ /******************** APW compatibility routines ********************/ #ifdef APW /* * Normally a C library function to print out a description of the most * recent system (non-toolbox, non-ProDOS) error. Exists under UNIX and * MS C 5.1, so I'm assuming it exists most everywhere else... */ void perror(errstr) char *errstr; { fflush(stdout); if ( (errno > 0) && (errno < sys_nerr) ) { /* known APW error? */ fprintf(stderr, "%s: %s\n", errstr, sys_errlist[errno]); } else { fprintf(stderr, "%s: ", errstr); fflush(stderr); ERROR( errno ); } Quit (-1); } /* Check for //gs toolbox errors; all are fatal */ void ToolErrChk() { int err = _toolErr; if (err) { if (err < MPErr) { /* was a ProDOS error? */ fprintf(stderr, "Error: $%.2x %s\n", (char) err, ProDOSErr[err]); } else { fprintf(stderr, "Tool err ($%.4x): ", err); fflush(stderr); ERROR( err ); } Quit (-1); } } #endif /* APW */ /******************** miscellaneous string routines ********************/ /* * Compare strings, ignoring case (may be in standard C lib; stricmp()?) */ int strcasecmp(str1, str2) register char *str1, *str2; { register char one, two; register int val; for ( ; *str1 && *str2; str1++, str2++) { one = (isupper(*str1) ? tolower(*str1) : *str1); two = (isupper(*str2) ? tolower(*str2) : *str2); if (val = two - one) return (val); } if (!(*str1) && !(*str2)) /* both zero -> equivalent */ return (0); else { /* one is shorter; return result */ one = (isupper(*str1) ? tolower(*str1) : *str1); two = (isupper(*str2) ? tolower(*str2) : *str2); return (two - one); } } int strncasecmp(str1, str2, num) register char *str1, *str2; int num; { register int i; register char one, two; register int val; /* keep going 'til no more registers... */ for (i = 0; (i < num) && (*str1) && (*str2); i++, str1++, str2++) { one = (isupper(*str1) ? tolower(*str1) : *str1); two = (isupper(*str2) ? tolower(*str2) : *str2); if (val = two - one) return (val); } if (i == num) /* first num characters are equal, so return zero */ return (0); else { /* one ended early; return result */ one = (isupper(*str1) ? tolower(*str1) : *str1); two = (isupper(*str2) ? tolower(*str2) : *str2); return (two - one); } } /******************* file-related routines ********************/ /* * Do operating system-dependent CREATE stuff * * Creates a NuFX archive file, with type info where necessary. * Does not leave file open. */ void ArcfiCreate(filename) char *filename; { static char *procName = "ArcfiCreate"; #ifdef UNIX int fd; if ((fd = open(filename, O_CREAT|O_RDWR, (mode_t) WPERMS)) < 0) Fatal("Unable to create file", procName); close(fd); #else # ifdef APW FileRec create_p; c2pstr(filename); create_p.pathname = filename; create_p.fAccess = 0x00e3; create_p.fileType = 0x00e0; /* LBR */ create_p.auxType = 0x8002; /* SHK */ create_p.storageType = 0x0001; create_p.createDate = 0x0000; /* let ProDOS fill in the blanks */ create_p.createTime = 0x0000; CREATE( &create_p ); ToolErrChk(); p2cstr(filename); # endif /* APW */ # ifdef MSDOS int fd; if ((fd = open(filename, O_CREAT|O_RDWR, (mode_t) WPERMS)) < 0) Fatal("Unable to create file", procName); close(fd); # endif /* MSDOS */ # ifndef APW # ifndef MSDOS int fd; if ((fd = open(filename, O_CREAT|O_RDWR, (mode_t) WPERMS)) < 0) Fatal("Unable to create file", procName); close(fd); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } /* * Determine if a file already exists. */ BOOLEAN Exists(filename) char *filename; { static char *procName = "Exists"; #ifdef UNIX struct stat sm; if (stat(filename, &sm) < 0) { if (errno == ENOENT) /* if doesn't exist, then okay */ return (FALSE); else { /* some other problem killed stat(), probably serious */ fprintf(stderr, "Unable to stat() '%s'\n", filename); Fatal("Bad stat()", procName); /*serious prob*/ /*NOTREACHED*/ } } else /* successful call - file exists */ return (TRUE); #else # ifdef APW FileRec info_p; /* check if file exists, is dir */ int err; c2pstr(filename); info_p.pathname = filename; GET_FILE_INFO( &info_p ); err = _toolErr; p2cstr(filename); if (err == pathNotFound || err == fileNotFound) return (FALSE); else if (!err) return (TRUE); else { _toolErr = err; ToolErrChk(); return (TRUE); } # endif /* APW */ # ifdef MSDOS struct stat sm; if (stat(filename, &sm) < 0) { if (errno == ENOENT) /* if doesn't exist, then okay */ return (FALSE); else /* some other problem killed stat(), probably serious */ fprintf(stderr, "Unable to stat() '%s'\n", filename); Fatal("Bad stat()", procName); /*serious prob*/ } else /* successful call - file exists */ return (TRUE); # endif /* MSDOS */ # ifndef APW # ifndef MSDOS printf("Need [other] Exists()\n"); /* +PORT+ */ return (FALSE); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } /* * Generate a temporary file name (system dependent). * Assumes space is allocated for buffer. */ char * MakeTemp(buffer) char *buffer; { static char *procName = "MakeTemp"; #ifdef UNIX extern char *mktemp(); strcpy(buffer, "nulb.tmpXXXXXX"); return (mktemp(buffer)); #else # ifdef APW int idx = 0; do { sprintf(buffer, "nulb.tmp%d", idx++); } while (Exists(buffer)); return (buffer); # endif /* APW */ # ifdef MSDOS extern char *mktemp(); strcpy(buffer, "nulbXXXX.tmp"); return (mktemp(buffer)); # endif /* MSDOS */ # ifndef APW # ifndef MSDOS strcpy(buffer, "nulb.tmp"); /* +PORT+ */ return (buffer); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } #ifdef NO_RENAME /* * This is a replacement for the library call, in case somebody's C library * doesn't have it. */ int rename(fromname, toname) { if (link(fromname, toname) < 0) return (-1); if (unlink(fromname) < 0) return (-1); } #endif /* * Rename a file. */ void Rename(fromname, toname) char *fromname, *toname; { static char *procName = "Rename"; #ifdef UNIX if (Exists(toname)) { fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", prgName, fromname, toname); fflush(stderr); } # ifdef AOSVS /* BAK 04/30/90 */ printf("Work on AOS/VS rename command\n"); /* BAK 04/30/90 */ # else /* BAK 04/30/90 */ else { if (rename(fromname, toname) < 0) { /* this should "never" fail */ fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", prgName, fromname, toname); Fatal("Bad rename()", procName); /*serious prob*/ } } # endif #else # ifdef APW PathNameRec cpath_p; if (Exists(toname)) { fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", prgName, fromname, toname); fflush(stderr); return; } cpath_p.pathname = fromname; cpath_p.newPathname = toname; c2pstr(fromname); c2pstr(toname); CHANGE_PATH( &cpath_p ); ToolErrChk(); p2cstr(fromname); p2cstr(toname); # endif /* APW */ # ifdef MSDOS if (Exists(toname)) { fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", prgName, fromname, toname); fflush(stderr); return; } printf("Work on MSDOS rename command\n"); # endif /* MSDOS */ # ifndef APW # ifndef MSDOS if (Exists(toname)) { fprintf(stderr, "\n%s: WARNING: Unable to rename '%s' as '%s'\n", prgName, fromname, toname); fflush(stderr); return; } printf("Need [other] rename command\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /*UNIX*/ } /******************** date/time routines ********************/ /* * Expand date/time from file-sys dependent format to eight byte NuFX format. * tptr is filesys format, TimePtr is NuFX format */ void ExpandTime(tptr, TimePtr) /* (BSD) UNIX version */ onebyt *tptr; /* usually points to a time_t (long) */ Time *TimePtr; { #ifdef UNIX time_t *tp = (time_t *) tptr; struct tm *unixt; unixt = localtime(tp); /* expand time_t into components */ TimePtr->second = unixt->tm_sec; TimePtr->minute = unixt->tm_min; TimePtr->hour = unixt->tm_hour; TimePtr->year = unixt->tm_year; TimePtr->day = unixt->tm_mday -1; /* want 0-xx, not 1-xx */ TimePtr->month = unixt->tm_mon; TimePtr->extra = 0; TimePtr->weekDay = unixt->tm_wday +1; /* Sunday = 1, not 0 like UNIX */ #else # ifdef APW /* APW version */ twobyt date, time; date = (twobyt)tptr[0] + ((twobyt)tptr[1] << 8); time = (twobyt)tptr[2] + ((twobyt)tptr[3] << 8); TimePtr->second = 0; /* not stored in ProDOS file info */ TimePtr->minute = (char) time; /* truncated to char */ TimePtr->hour = time >> 8; TimePtr->year = date >> 9; TimePtr->day = (date & 0x1f) - 1; TimePtr->month = ((date & 0x01e0) >> 5) - 1; TimePtr->extra = 0; TimePtr->weekDay = 0; # endif /* APW */ # ifdef MSDOS struct tm *newtime; time_t *tp = (time_t *) tptr; newtime = localtime (tp); TimePtr->second = (onebyt)newtime->tm_sec; TimePtr->minute = (onebyt)newtime->tm_min; TimePtr->hour = (onebyt)newtime->tm_hour; TimePtr->year = (onebyt)newtime->tm_year; TimePtr->day = (onebyt)newtime->tm_mday - 1; TimePtr->month = (onebyt)newtime->tm_mon; TimePtr->extra = 0; TimePtr->weekDay= (onebyt)newtime->tm_wday + 1; # endif /* MSDOS */ # ifndef APW # ifndef MSDOS printf("Need [other] time-expander\n"); /* +PORT+ */ TimePtr->second = 0; TimePtr->minute = 0; TimePtr->hour = 0; TimePtr->year = 0; TimePtr->day = 0; TimePtr->month = 0; TimePtr->extra = 0; TimePtr->weekDay = 0; # endif /* none1 */ # endif /* none2 */ #endif /* UNIX */ } /* * Get current time, put in struct */ Time * GetTime() { static Time t; #ifdef UNIX struct tm *unixt; time_t now = time(NULL); unixt = localtime(&now); t.second = unixt->tm_sec; t.minute = unixt->tm_min; t.hour = unixt->tm_hour; t.year = unixt->tm_year; t.day = unixt->tm_mday -1; /* want 0-xx, not 1-xx */ t.month = unixt->tm_mon; t.extra = 0; t.weekDay = unixt->tm_wday +1; /* Sunday = 1, not 0 like UNIX */ /* return (&t) */ #else # ifdef APW t = ReadTimeHex(t); /* return (&t) */ # endif /* APW */ # ifdef MSDOS struct tm *pctime; time_t now = time(NULL); pctime = localtime(&now); t.second = (onebyt)pctime->tm_sec; t.minute = (onebyt)pctime->tm_min; t.hour = (onebyt)pctime->tm_hour; t.year = (onebyt)pctime->tm_year; t.day = (onebyt)pctime->tm_mday -1; /* want 0-xx, not 1-xx */ t.month = (onebyt)pctime->tm_mon; t.extra = 0; t.weekDay= (onebyt)pctime->tm_wday +1; /* Sunday = 1, not 0 */ /* return (&t) */ # endif /* MSDOS */ # ifndef APW # ifndef MSDOS printf("\nNeed [other] GetTime\n"); /* +PORT+ */ t->second = 0; t->minute = 0; t->hour = 0; t->year = 0; t->day = 0; t->month = 0; t->filler = 0; t->weekDay = 0; /* return (&t) */ # endif /* none1 */ # endif /* none2 */ #endif /* UNIX */ return (&t); } /* * Convert a NuFX Time struct to a compact system-dependent format * * This is used to set a file's date when extracting. Most systems don't * dedicate 8 bytes to storing the date; this reduces it to the format * used by a "set_file_date" command. */ long ReduceTime(tptr) Time *tptr; { #ifdef UNIX long t = timecvt(tptr); return (t ? t : time(NULL)); /* if stored time is invalid, */ /* return current time */ #else # ifdef APW twobyt date, time; long val; date = ((twobyt)tptr->year << 9) | ((((twobyt)tptr->month)+1) << 5) | (((twobyt)tptr->day)+1); time = ((twobyt)tptr->hour << 8) | ((twobyt)tptr->minute); val = (long) date + ((long) time << 16); return (val); # endif /* APW */ # ifdef MSDOS return (time(NULL)); /* not sure what to do, return current : RBH */ # endif /* MSDOS */ #ifndef APW #ifndef MSDOS printf("Need [other] ReduceTime\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } and time_t into components */ TimePtr->second = unixt->tm_sec; TimePtr->minute = unixt->tm_min; TimePtr->hour = unixt->tm_hour; TimePtr->year = unixt->tm_year; TimePtr->day = unixt->tm_mday -1; /* want 0-xx, not 1-xx */ TimePtr->month = unixtnulib/nuext.c 644 4170 1464 46472 5324373333 6662 /* * nuext.c - operations which extract from a NuFX archive * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "NuMain" #endif #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 /* need under MSC6? */ # 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); 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') { *str &= 0x7f; /* clear hi bit */ if (*str == '/') *str = '.'; if (++idx > 255) { *str = '\0'; break; } /* MAXNAMELEN? */ str++; } #else # ifdef APW static char *legal = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789."; /* assumes ProDOS limits, not GS/OS */ *str &= 0x7f; 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 char *ostr = str, *prev_dot = NULL; while (*str != '\0') { *str &= 0x7f; /* clear hi bit */ if (*str == '/') *str = '_'; if (*str == '\\') *str = '_'; if (*str == '!') *str = '_'; if (*str == ':') *str = '_'; if (*str == '.') { if (prev_dot != NULL) *prev_dot = '_'; prev_dot = str; } if (++idx > 255) { *str = '\0'; break; } str++; } /* now limit the chars before the '.' to 8, and after to 3 */ /* (if no dot, cut it at 8) */ if (prev_dot == NULL) { if (strlen(str) > 8) str[8] = '\0'; } else { *prev_dot = '\0'; if (strlen(prev_dot+1) > 3) *(prev_dot+4) = '\0'; if (strlen(ostr) > 8) { *prev_dot = '.'; while (*prev_dot) *((ostr++) + 8) = *(prev_dot++); *((ostr++) + 8) = *(prev_dot++); } else *prev_dot = '.'; } # 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++] = '\\'; /* was '\' */ *ptr = '\\'; /* (ditto) */ # 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, (mode_t)WPERMS)) < 0) Fatal("Unable to open target path", procName); if (lseek(arcfd, (off_t) 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, (off_t) 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 obsolete ASCII text thread (ignored)\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, (off_t) 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; case 0x0002: /* standard Apple IIgs icon */ printf("Found Apple IIgs Icon thread (ignored)\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 (ignored)\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 */ if (!namecount) { extall = TRUE; lentab = (int *) NULL; } else { lentab = (int *) Malloc(sizeof(int) * namecount); for (idx = 0; idx < namecount; idx++) /* calc. once (for efficiency) */ lentab[idx] = strlen(names[idx]); } MHptr = archive->MHptr; RNodePtr = archive->RNodePtr; /* 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 */ } 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 = nulib/numain.c 644 4170 1464 41255 5324641244 6777 /* * numain.c - shell-based front end for NuLib * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "main" #endif static char *header = "NuLib v3.24 Januray 1993 Freeware Copyright 1989-93 By Andy McFadden"; #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 doMessages; /* do comments instead of data */ int transfrom; /* how to do CR<->LF translation (from what?) (-1 = off) */ int transto; /* translate to ? */ int packMethod; /* how to pack a file */ int diskData; /* store files as if they are disk images */ 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 */ /* * Print simple usage info */ static void Usage(argv0) char *argv0; { printf("\nUsage: %s option[suboptions] 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-I (ShrinkIt) shk Y Y\n"); printf(" 3: Dynamic LZW-II (ShrinkIt) sh2 N Y\n"); printf(" 4: UNIX 12-bit compress u12 Y Y\n"); printf(" 5: UNIX 16-bit compress u16 Y Y\n"); printf("The default is #2\n"); printf("\nText conversion methods (during extraction):\n"); printf(" 0: Convert from CR to this system (ProDOS files)\n"); printf(" 1: Convert from LF to this system (UNIX files)\n"); printf(" 2: Convert from CRLF to this system (MS-DOS files)\n"); } else if (INDEX(options+1, 'w')) { /* print author info */ printf("%s\n", header); printf( "Internet: fadden@uts.amdahl.com Usenet: ...!amdahl!fadden\n"); printf("\nShrinkIt and NuFX standard by Andy Nicholas.\n"); printf( "ShrinkIt LZW compression by Kent Dickey. LZW-II (a modified version of\n"); printf( " Kent's algorithm) designed by Andy Nicholas. C LZW-II decoder by Kent\n"); printf( " Dickey and Frank Petroski (independently and simultaneously).\n"); printf("\nUNIX compress code adapted from COMPRESS v4.3.\n"); printf( "\nBinary 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 and Bruce Kahn.\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 if (INDEX(options+1, 's')) { /* suboption info */ printf("%s\n", header); printf("\nUsual meaning of suboptions:\n"); printf(" c - compression type, followed by a number\n"); printf( " f - file/aux type to add, followed by file/aux type spec\n"); printf(" d - store the files, but mark them as disk images\n"); printf(" i - interactive; prompt before overwriting file\n"); printf(" m - messages (add/extract comments instead of data)\n"); printf(" r - don't recursively descend subdirectories\n"); printf(" s - storage type (store as compressed w/o compressing), "); printf("followed by number\n"); printf( " t - text translation (CR<->LF), followed by conversion mode\n"); printf(" u - store as uncompressed (same as c0)\n"); printf(" v - verbose mode\n"); #ifndef NO_BLU printf(" x - extract during Binary II operations\n"); #endif printf(" + - match partial pathnames for extract and delete\n"); printf("\nTable of contents suboptions:\n"); printf(" v - verbose output (same as V option)\n"); printf(" a - ARC/ZOO style format\n"); printf(" z - full output, prints all aspects of archive\n"); printf("\nSample shell variable command (csh):\n"); printf( " setenv NULIBOPT=verbose,interactive,type=SRC,aux=000a,compress=5\n"); printf(" (default is non-verbose, non-interactive, type=NON, "); printf( "aux=0000, compress=2)\n"); } else { /* default help screen */ printf("%s\n", header); printf( "\nUsage: %s option[suboptions] archive-name [filespec]\n", argv0); printf("Option must be one of:\n"); printf(" a[vucsrfd] add to archive\n"); #ifndef NO_BLU printf(" b[xvti] Binary II archive operations\n"); #endif printf( " c[vucsrfd] create archive (add, but suppress 'create' message)\n"); printf(" d[v+] delete file from archive\n"); printf(" f[vucsrfd] freshen files in archive\n"); printf(" h[snw] show help screen (subopt/numbers/who's who)\n"); printf(" i[v] verify archive integrity\n"); printf(" m[vucsrfd] move files to archive (add, delete original)\n"); printf(" p[vmt+] print archived file to stdout\n"); printf(" t[vaz] display table of contents\n"); printf(" u[vucsrfd] update files in archive\n"); printf(" v verbose listing (ProDOS 8 ShrinkIt format)\n"); printf(" x,e[vumti+] extract from archive\n"); } } /* * Check machine dependencies */ static void CheckMach() { onebyt one; onebyt *oneptr; twobyt two; fourbyt four; /* * If you get an error on either of these lines, then you need to * specify definitions for them in nudefs.h (see the comments in the * file for more info). */ off_t off; mode_t mode; #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 */ doMessages = FALSE; /* do comments instead of data */ packMethod = 0x0002;/* ShrinkIt LZW */ diskData = FALSE; /* store as ordinary files */ 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, "compress=")) >= 0) { off += 9; if (off+1 > strlen(envptr)) { fprintf(stderr, "Error with 'compress=n' in NULIBOPT var\n"); Quit (-1); } packMethod = atoi(envptr+off); } 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 'a': /* ARC/ZOO output format */ /* do nothing */ 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 'd': /* treat as disk images instead of ordinary files */ diskData = 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 'm': /* do messages instead of data */ doMessages = 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; case 'z': /* in view files */ /* do nothing */ break; default: /* unknown */ fprintf(stderr, "%s: unknown subopt '%c'\n", prgName, 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 (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]); if (argc < 3) { /* no archive file specified; show help screen */ if (argv[1][0] == 'h') /* could be HS, 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 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); } Method = 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 */ dnulib/nupak.c 644 4170 1464 35363 5315526417 6635 /* * nupak.c - interface to the compression routines * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #define TRY_II #ifdef APW segment "Compress" #endif #include "nudefs.h" #include /*#include */ /* "nucomp.h" includes for us */ #include "nuread.h" /* need THblock */ #include "nucomp.h" /* includes "nucompfn.h" + "types.h" */ #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 put 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. * * Since we're only using version 0 records, we don't need to propagate the * thread_crc. * * Compression routines must do the following: * - compress data from one file descriptor to another, given two seeked * file descriptors and a length value. They may not rely on EOF conditions * for either file. * - return the packing method actually used. If they cope with failure * by starting over with something different, the successful method should * be returned. Failure may be handled in the switch statement below. */ 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; twobyt retval = thread_format; /* default = successful pack */ long srcposn, dstposn; static char *procName = "PackFile"; 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; case 0x0003: /* LZW II (ShrinkIt) */ if (verbose) { printf("[can't do LZW II; storing]..."); fflush(stdout); } else { printf("WARNING: can't do LZW II; files stored uncompressed\n"); } FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; retval = 0x0000; /* uncompressed */ break; case 0x0004: /* UNIX 12-bit compress */ #ifdef NO_UCOMP if (verbose) { printf("[can't do 12-bit UNIX compress; storing]..."); fflush(stdout); } else { printf( "WARNING: can't do 12-bit compress; files stored uncompressed\n"); } FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; retval = 0x0000; /* uncompressed */ #else maxbits = 12; /* global compress parameter */ if (verbose) { printf("compressing..."); fflush(stdout); } /* packedSize set by compress() */ if (u_compress(srcfd, dstfd, length) == OK) retval = 0x0004; else retval = 0x0004; /* FIX this */ #endif break; case 0x0005: /* UNIX 16-bit compress */ #ifdef NO_UCOMP if (verbose) { printf("[can't do 16-bit UNIX compress; storing]..."); fflush(stdout); } else { printf( "WARNING: can't do 16-bit compress; files stored uncompressed\n"); } FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; retval = 0x0000; /* uncompressed */ #else maxbits = 16; /* global compress parameter */ if (verbose) { printf("compressing..."); fflush(stdout); } /* packedSize set by compress() */ srcposn = (long) lseek(srcfd, (off_t) 0, S_REL); /* save posn */ dstposn = (long) lseek(dstfd, (off_t) 0, S_REL); if (u_compress(srcfd, dstfd, length) == OK) { /* compress succeeded */ retval = 0x0005; } else { /* compression failed */ if (verbose) { printf("storing..."); fflush(stdout); } lseek(srcfd, (off_t) srcposn, S_ABS); /* reposn files */ lseek(dstfd, (off_t) dstposn, S_ABS); FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; retval = 0x0000; } #endif 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. * * (hacked to unpack disks, also. Forces the thread_eof to be the total * size of the disk, since ShrinkIt doesn't really define it, esp for DOS 3.3 * disks). * * 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. * * New uncompression routines should have the following characteristics: * - they should be able to uncompress a range of bytes from one file * to another given two seeked file descriptors and a length parameter. * - they should return TRUE if they succeed and FALSE otherwise. Special * condition codes can be handled in the switch statement below. */ int UnpackFile(srcfd, dstfd, THptr, thread_format, buffer) int srcfd; /* source file descriptor (must be open & lseek()ed) */ int dstfd; /* destination file descriptor (must be open & lseek()ed) */ THblock *THptr; /* pointer to thread structure */ int thread_format; /* how to unpack the bytes (NOT THptr->thread_format) */ onebyt *buffer; { long length; fourbyt thread_eof, /* #of bytes to output */ comp_thread_eof; /* #of bytes in source */ twobyt thread_crc; BOOLEAN retval = TRUE; /* default to success */ static char *procName = "UnpackFile"; thread_eof = THptr->thread_eof; comp_thread_eof = THptr->comp_thread_eof; thread_crc = THptr->thread_crc; length = (long) comp_thread_eof; /* type checking goes easier */ 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, (off_t)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 (I)..."); fflush(stdout); } unpak_SHK(srcfd, dstfd, comp_thread_eof, thread_eof, buffer, FALSE, thread_crc); break; case 0x0003: /* LZW II (ShrinkIt) */ #ifdef TRY_II if (verbose) { printf("unshrinking (II)..."); fflush(stdout); } unpak_SHK(srcfd, dstfd, comp_thread_eof, thread_eof, buffer, TRUE, thread_crc); #else if (verbose) { printf("[can't unshrink type II - aborting]..."); fflush(stdout); } else { printf( "ERROR: can't unshrink type II; 'sh2' files not extracted\n"); } lseek(srcfd, (off_t)length, S_REL); /* set file posn */ retval = FALSE; #endif break; case 0x0004: /* 12-bit UNIX compress */ #ifdef NO_UCOMP if (verbose) { printf("[can't undo 12-bit UNIX compress - aborting]..."); fflush(stdout); } else { printf( "ERROR: can't undo 12-bit UNIX compress; 'u12' files not extracted\n"); } lseek(srcfd, (off_t)length, S_REL); /* set file posn */ retval = FALSE; #else if (verbose) { printf("uncompressing..."); fflush(stdout); } if (u_decompress(srcfd, dstfd, (long) comp_thread_eof) != OK) retval = FALSE; #endif break; case 0x0005: /* 16-bit UNIX compress */ #ifdef NO_UCOMP if (verbose) { printf("[can't undo 16-bit UNIX compress - aborting]..."); fflush(stdout); } else { printf( "ERROR: can't undo 16-bit UNIX compress; 'u16' files not extracted\n"); } lseek(srcfd, (off_t)length, S_REL); /* set file posn */ retval = FALSE; #else if (verbose) { printf("uncompressing..."); fflush(stdout); } if (u_decompress(srcfd, dstfd, (long) comp_thread_eof) != OK) retval = FALSE; #endif break; default: fprintf(stderr, "Unknown uncompression method %d\n", thread_format); lseek(srcfd, (off_t)length, S_REL); /* set file posn */ retval = FALSE; break; } return (retval); } . Forces the thread_eof to be the total * size of the disk, since ShrinkIt doesn't really define it, esp for DOS 3.3 * disks). * * Set up stuff, then call the appropriate unpack routine. Leaves the srcfd * positioned past the data to be unpacked; the calling rnulib/nupdel.c 644 4170 1464 33333 5253231444 6773 /* * nudel.c/nuupd.c - operations which delete/update/freshen a NuFX archive * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "NuMain" #endif #include "nudefs.h" #include #include #ifdef BSD43 # include #else /* SYSV, APW, MSC */ # include #endif #ifdef UNIX # include # include #endif #ifdef APW # include # include /* ? */ # include # include /* APW string ops */ #endif #ifdef MSDOS # include # include # include # include #endif #include "nuread.h" #include "nuadd.h" /* AddFile(), etc. */ #include "nupak.h" /* uses PAKBUFSIZ */ #include "nupdel.h" #include "nuetc.h" static BOOLEAN dofreshen; /* do freshen instead of update? */ /* delete routines */ /* * Rebuild an archive, excluding files marked as deleted. * Does not use absolute position values; just seeks along. The archive * should be seeked just beyond the master header block. */ static void RebuildArchive(arcfd, archive, tmpname, remaining) int arcfd; ListHdr *archive; char *tmpname; long remaining; { int dstfd; /* destination filename */ onebyt *mptr; RNode *RNodePtr; TNode *TNodePtr; int rec, thread; long size; long master_eof; #ifdef APW FileRec create_p; #endif static char *procName = "RebuildArchive"; if (verbose) { printf("Building new archive file..."); fflush(stdout); } ArcfiCreate(tmpname); /* create file */ master_eof = (long) MHsize; if ((dstfd = open(tmpname, O_WRONLY | O_TRUNC | O_BINARY,(mode_t)WPERMS))<0) Fatal("Unable to open dest file", procName); if (lseek(dstfd, (off_t) MHsize, S_ABS) < 0) Fatal("Unable to lseek past dest mhblock", procName); RNodePtr = archive->RNodePtr; /* copy the surviving records to the destination file */ for (rec = 0; rec < archive->MHptr->total_records; rec++) { #ifdef APW if (STOP()) { printf("aborting.\n"); Quit(1); } #endif size = (long) RNodePtr->RHptr->attrib_count; size += (long) RNodePtr->filename_length; 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); } size += (long) THsize; size += TNodePtr->THptr->comp_thread_eof; TNodePtr = TNodePtr->TNext; } if (!RNodePtr->filename[0]) { if (lseek(arcfd, (off_t) size, S_REL) < 0) Fatal("Unable to seek past deleted record", procName); } else { FCopy(arcfd, dstfd, size, pakbuf, FALSE); master_eof += size; } RNodePtr = RNodePtr->RNext; /* move on to next record */ } mptr = MakeMHblock(archive, remaining, master_eof); /* build mheader */ if (lseek(dstfd, (off_t) 0, S_ABS) < 0) Fatal("Unable to seek back in dest file", procName); if (write(dstfd, mptr, MHsize) < MHsize) Fatal("Unable to write master header", procName); if (close(dstfd) < 0) Fatal("Unable to close archive", procName); if (verbose) printf("done.\n"); } /* * Delete files from archive * * Scan archive, deleting files which match the strings in "names". */ static void Delete(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 */ int len, *lentab; /* hold strlen() of all names */ char *pn; /* archived pathname */ long remaining; char *tmpname = (char *) Malloc(MAXFILENAME); static char *procName = "Delete"; archive = NuRead(filename); if ((arcfd = open(archive->arc_name, O_RDONLY | O_BINARY)) < 0) Fatal("Unable to open archive", procName); 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; remaining = MHptr->total_records; /* 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("WARNING: '%s' has unknown record version_number\n", pn); for (idx = 0; idx < namecount; idx++) { /* find file in archive */ /* try to match argument with first few chars of stored filename */ /* or the entire filename, depending on EXPAND flag */ if ((len >= lentab[idx]) && doExpand ? (!strncasecmp(pn, names[idx], lentab[idx])) : (!strcasecmp(pn, names[idx])) ) { if (verbose) printf("Marking '%s' as deleted.\n", pn); RNodePtr->filename[0] = '\0'; /* mark as deleted */ remaining--; break; /* out of filename matching for-loop */ } } RNodePtr = RNodePtr->RNext; /* move on to next record */ } if (remaining == MHptr->total_records) { if (verbose) printf("No files selected; archive not modified\n"); if (close(arcfd) < 0) Fatal("Source (archive) close failed", procName); Quit (0); } if (remaining == 0L) { printf("All files in archive marked for deletion..."); fflush(stdout); if (close(arcfd) < 0) Fatal("Source (archive) close failed", procName); #ifdef APW if (STOP()) { printf("aborting.\n"); Quit (1); } #endif printf(" deleteing archive file.\n"); if (unlink(archive->arc_name) < 0) Fatal("Unable to delete archive", procName); } else { tmpname = MakeTemp(tmpname); #ifdef APW if (STOP()) { printf("aborting.\n"); Quit (1); } #endif if (lseek(arcfd, (off_t) MHsize, S_ABS) < 0) Fatal("Unable to seek past master block", procName); RebuildArchive(arcfd, archive, tmpname, remaining); if (close(arcfd) < 0) Fatal("Source (archive) close failed", procName); if (verbose) { printf("Deleteing old archive file..."); fflush(stdout); } if (unlink(archive->arc_name) < 0) Fatal("Unable to delete original archive", procName); Rename(tmpname, archive->arc_name); if (verbose) printf("done.\n"); } free (tmpname); free (lentab); } /* * Entry point for deleteing files from archive. */ void NuDelete(filename, namecount, names, options) char *filename; int namecount; char **names; char *options; { static char *procName = "NuDelete"; /* presently does nothing */ Delete(filename, namecount, names); } /********** update routines **********/ /* * Updates the archive. * * Evaluate the command line arguments and compare them with the files in * the archive. Put the most recent copy of the file in a new archive file. * Essentially a combination of add and delete. * * This procedure is huge. Someday I may clean this up a bit... */ static void Update(archive, namecount, names) ListHdr *archive; int namecount; char **names; { int arcfd, dstfd; /* archive file descriptor */ static file_info *FIArray[MAXARGS]; /* entries malloc()ed by EvalArgs */ unsigned int rec; int idx, thread; MHblock *MHptr; /* Master Header block */ RNode *RNodePtr; /* Record Node */ TNode *TNodePtr; /* Thread block */ char *pn; /* archived pathname */ BOOLEAN keeparc, gotone; char *tmpname = (char *) Malloc(MAXFILENAME); Time *atptr, *ftptr; long a_dt, f_dt; long size; fourbyt totalrecs, master_eof; onebyt *mptr; twobyt *twoptr; static char *procName = "Update"; if ((arcfd = open(archive->arc_name, O_RDONLY | O_BINARY)) < 0) Fatal("Unable to open archive", procName); /* expand wildcards/subdirectories, and get info */ namecount = EvalArgs(namecount, names, FIArray, TRUE); if (!namecount) { if (verbose) printf("No files selected; archive not modified.\n"); Quit (0); } /* * For each file in the archive, check for an *exact* match with the * store_names in FIArray (case independent). If a match is found, * compare the dates, and copy/add the most recent file. If no match * is found, copy the file. After all archived files have been processed, * add all remaining files specified on the command line (unless the * F)reshen option is used, in which case this exits). */ MHptr = archive->MHptr; RNodePtr = archive->RNodePtr; gotone = FALSE; /* mark files that will be replaced */ for (rec = 0; rec < MHptr->total_records; rec++) { #ifdef APW if (STOP()) { printf("aborting.\n"); Quit (1); } #endif pn = RNodePtr->filename; if (RNodePtr->RHptr->version_number > MAXVERS) printf("WARNING: '%s' has unknown record version_number\n", pn); for (idx = 0; idx < namecount; idx++) { /* find file in archive */ /* try to match argument with first few chars of stored filename */ if (!strcasecmp(pn, FIArray[idx]->store_name)) { atptr = &RNodePtr->RHptr->mod_when; ftptr = &FIArray[idx]->mod_dt; /* compare month/year [ I think it's best-case faster... ] */ a_dt = (atptr->year * 12) + atptr->month; f_dt = (ftptr->year * 12) + ftptr->month; if (a_dt > f_dt) /* archive is more recent? */ keeparc = TRUE; else if (a_dt < f_dt) /* file is more recent? */ keeparc = FALSE; else { /* year & month match, check rest */ a_dt = (atptr->day * 86400L) + (atptr->hour * 3600) + (atptr->minute * 60) + (atptr->second); f_dt = (ftptr->day * 86400L) + (ftptr->hour * 3600) + (ftptr->minute * 60) + (ftptr->second); if (a_dt < f_dt) keeparc = FALSE; else /* (a_dt >= f_dt) */ keeparc = TRUE; } if (!keeparc) { /* not keeping; mark as being replaced */ #ifndef APW /* APW uses actual filetype; other systems keep old */ FIArray[idx]->fileType = RNodePtr->RHptr->file_type; FIArray[idx]->auxType = RNodePtr->RHptr->extra_type; #endif RNodePtr->RHptr->version_number = 65535; /*can't do fname*/ twoptr = (twobyt *) RNodePtr->filename; *twoptr = idx; /* null filename -> problems */ gotone = TRUE; } FIArray[idx]->marked = TRUE; /* MARK as processed */ } } RNodePtr = RNodePtr->RNext; /* move on to next record */ } totalrecs = MHptr->total_records; /* none will be deleted */ if (!dofreshen) { /* add new files? */ for (idx = 0; idx < namecount; idx++) { /* handle unmatched args */ if (!FIArray[idx]->marked) { gotone = TRUE; totalrecs++; /* count new ones too */ } } } if (!gotone) { if (verbose) printf("No files need updating; archive not modified\n"); if (close(arcfd) < 0) Fatal("Source (archive) close failed", procName); Quit (0); } /* * Rebuild archive file */ if (verbose) printf("Building new archive file...\n"); tmpname = MakeTemp(tmpname); ArcfiCreate(tmpname); master_eof = (long) MHsize; if (lseek(arcfd, (off_t) MHsize, S_ABS) < 0) Fatal("Bad archive seek", procName); if ((dstfd = open(tmpname, O_RDWR | O_TRUNC | O_BINARY)) < 0) Fatal("Unable to open dest file", procName); if (lseek(dstfd, (off_t) MHsize, S_ABS) < 0) Fatal("Bad dest seek", procName); /* leave space for later */ RNodePtr = archive->RNodePtr; for (rec = 0; rec < MHptr->total_records; rec++) { size = (long) RNodePtr->RHptr->attrib_count; size += (long) RNodePtr->filename_length; 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); } size += (long) THsize; size += TNodePtr->THptr->comp_thread_eof; TNodePtr = TNodePtr->TNext; } /* we now know the size; either copy the old or replace with new */ if (RNodePtr->RHptr->version_number != 65535) { /* file not replaced */ /* if (verbose) { * printf("Copying '%s'...", RNodePtr->filename); * fflush(stdout); * } */ FCopy(arcfd, dstfd, size, pakbuf, FALSE); master_eof += (fourbyt) size; /* if (verbose) printf("done.\n"); */ } else { /* file replaced; skip orig and add new */ if (lseek(arcfd, (off_t) size, S_REL) < 0) Fatal("Bad skip seek", procName); twoptr = (twobyt *) RNodePtr->filename; idx = *twoptr; if (verbose) printf("Replacing/"); /* +"Adding 'filename'..." */ master_eof += AddFile(dstfd, FIArray[idx]); } RNodePtr = RNodePtr->RNext; /* move on to next record */ } if (!dofreshen) { for (idx = 0 ; idx < namecount; idx++) { #ifdef APW if (STOP()) Quit(1); /* check for OA-. */ #endif if (!FIArray[idx]->marked) { master_eof += AddFile(dstfd, FIArray[idx]); } } } /* build master header */ mptr = MakeMHblock(archive, totalrecs, master_eof); if (lseek(dstfd, (off_t) 0, S_ABS) < 0) Fatal("Bad lseek for master header", procName); if (write(dstfd, mptr, MHsize) < MHsize) Fatal("Unable to write master header", procName); if (close(arcfd) < 0) Fatal("Source (old archive) close failed", procName); if (close(dstfd) < 0) Fatal("Destination (new archive) close failed", procName); if (verbose) { printf("Deleteing old archive file..."); fflush(stdout); } if (unlink(archive->arc_name) < 0) Fatal("Unable to delete original archive", procName); Rename(tmpname, archive->arc_name); if (verbose) printf("done.\n"); free (tmpname); } /* * Update files in archive * * This part just evaluates the options, sets parms, and calls Update(). */ void NuUpdate(filename, namecount, names, options) char *filename; int namecount; char **names; char *options; { ListHdr *archive; static char *procName = "NuUpdate"; if (*options == 'f') dofreshen = TRUE; else dofreshen = FALSE; /* change T subopt to convert FROM current system TO */ if (transfrom >= 0) { transto = transfrom; transfrom = -1; } archive = NuRead(filename); Update(archive, namecount, names); } dePtr->RHptr->version_number = 65535; /*can't do fname*/ twoptr = (twobyt *) RNodePtr->filename; *twoptr = idx; /* null filename -> problems */ gotone = TRUE; } FIArray[idx]->marked = TRUE; /* MARK as processed */ } } RNodePtr = RNodePtr->RNext; /* move on to nulib/nuread.c 644 4170 1464 45566 5253231452 6774 /* * nuread.c - read NuFX archives (header info only) into structures * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "NuMain" #endif #include "nudefs.h" #include #include #include #ifdef UNIX # include #endif #ifdef MSDOS /* For file IO */ # include /* errno, among others */ # include # include # include # include #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 "" /* quick proc to save x00 bytes of static storage */ void OtherArc(str1, str2) char *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) register onebyt *srcptr, *destptr; 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 */ register onebyt *ptr; /* pointer to start of data buffer */ register int count; /* number of bytes to scan through - note 64K max */ { #ifndef CRC_TAB register int x; #endif 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=0, 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, (off_t) 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, (off_t) 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, (off_t) 0, 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, (off_t) TNodePtr->fileposn, S_ABS) < 0) Fatal("Unable to seek back after fn", procName); } if (lseek(fd, (off_t) 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"; char *cp; 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, "", 4)) /* system V arch */ OtherArc("a library archive (Sys V)", "ar"); if (!strncmp((char *) filebuf, "!", 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'; for (cp = RNodePtr->filename; *cp; cp++) *cp &= 0x7f; /*strip high bits from old-style names*/ } 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, (off_t) 0, S_REL)) < 0) Fatal("Bad final lseek()", procName); if (close(fd) < 0) { Fatal("Bad close", procName); } if (MHptr->master_version > 0x0000) { if ((fourbyt) MHptr->master_eof != (fourbyt) 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); } irst = FALSE; } else { /* allocate next, and go on */ RNodePtr->RNext = (RNode *) Malloc(sizeof(RNode)); /* next Rnode */ RNodnulib/nushk.c 644 4170 1464 72322 5324372640 6640 /* * nushk.c - P8 ShrinkIt compress/uncompress routines * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "Compress" #endif #include "nudefs.h" #include #include #ifdef UNIX # include #endif #ifdef MSDOS /* For file IO */ # include # include # include # include #endif #include "nuread.h" /* need CalcCRC() */ #include "nupak.h" #include "nuetc.h" #ifdef UNIX # ifdef SYSV # define BCOPY(src, dst, count) memcpy(dst, src, count) # else # define BCOPY(src, dst, count) bcopy(src, dst, count); # endif #else # ifdef APW # define BCOPY(src, dst, count) memcpy(dst, src, count) # endif # ifdef MSDOS /* you may need to change this to memcpy() */ # define BCOPY(src, dst, count) bcopy(src, dst, count); /*# define BCOPY(src, dst, count) memcpy(dst, src, count)*/ # endif /* +PORT+ */ #endif #define BLKSIZ 4096 /*#define DEBUG /* do verbose debugging output */ /*#define DEBUG1 /* debugging output in main routine */ static onebyt *ibuf; /* large buffer (usually set to packBuffer) */ onebyt lbuf[BLKSIZ+7]; /* temporary buffer for storing data after LZW */ onebyt rbuf[BLKSIZ+7]; /* temporary buffer for storing data after RLE */ /* * P8 ShrinkIt compression routines * Copyright 1989 Kent Dickey * * C translation by Kent Dickey / Andy McFadden */ #define ESCAPE_CHAR 0xdb #define HSIZE 5119 /* Must be prime */ typedef struct ht { int entry; int prefix; onebyt chr; }; struct ht *htab = NULL; /* allocated first time through */ int s_at_bit; int s_at_byte; int mask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff }; int bit_tab[] = { 0,9,10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12 }; /* hash function from UNIX compress */ int hashf[256] = { 0x0000, 0x0204, 0x0408, 0x060c, 0x0810, 0x0a14, 0x0c18, 0x0e1c, 0x0020, 0x0224, 0x0428, 0x062c, 0x0830, 0x0a34, 0x0c38, 0x0e3c, 0x0040, 0x0244, 0x0448, 0x064c, 0x0850, 0x0a54, 0x0c58, 0x0e5c, 0x0060, 0x0264, 0x0468, 0x066c, 0x0870, 0x0a74, 0x0c78, 0x0e7c, 0x0080, 0x0284, 0x0488, 0x068c, 0x0890, 0x0a94, 0x0c98, 0x0e9c, 0x00a0, 0x02a4, 0x04a8, 0x06ac, 0x08b0, 0x0ab4, 0x0cb8, 0x0ebc, 0x00c0, 0x02c4, 0x04c8, 0x06cc, 0x08d0, 0x0ad4, 0x0cd8, 0x0edc, 0x00e0, 0x02e4, 0x04e8, 0x06ec, 0x08f0, 0x0af4, 0x0cf8, 0x0efc, 0x0100, 0x0304, 0x0508, 0x070c, 0x0910, 0x0b14, 0x0d18, 0x0f1c, 0x0120, 0x0324, 0x0528, 0x072c, 0x0930, 0x0b34, 0x0d38, 0x0f3c, 0x0140, 0x0344, 0x0548, 0x074c, 0x0950, 0x0b54, 0x0d58, 0x0f5c, 0x0160, 0x0364, 0x0568, 0x076c, 0x0970, 0x0b74, 0x0d78, 0x0f7c, 0x0180, 0x0384, 0x0588, 0x078c, 0x0990, 0x0b94, 0x0d98, 0x0f9c, 0x01a0, 0x03a4, 0x05a8, 0x07ac, 0x09b0, 0x0bb4, 0x0db8, 0x0fbc, 0x01c0, 0x03c4, 0x05c8, 0x07cc, 0x09d0, 0x0bd4, 0x0dd8, 0x0fdc, 0x01e0, 0x03e4, 0x05e8, 0x07ec, 0x09f0, 0x0bf4, 0x0df8, 0x0ffc, 0x0200, 0x0004, 0x0608, 0x040c, 0x0a10, 0x0814, 0x0e18, 0x0c1c, 0x0220, 0x0024, 0x0628, 0x042c, 0x0a30, 0x0834, 0x0e38, 0x0c3c, 0x0240, 0x0044, 0x0648, 0x044c, 0x0a50, 0x0854, 0x0e58, 0x0c5c, 0x0260, 0x0064, 0x0668, 0x046c, 0x0a70, 0x0874, 0x0e78, 0x0c7c, 0x0280, 0x0084, 0x0688, 0x048c, 0x0a90, 0x0894, 0x0e98, 0x0c9c, 0x02a0, 0x00a4, 0x06a8, 0x04ac, 0x0ab0, 0x08b4, 0x0eb8, 0x0cbc, 0x02c0, 0x00c4, 0x06c8, 0x04cc, 0x0ad0, 0x08d4, 0x0ed8, 0x0cdc, 0x02e0, 0x00e4, 0x06e8, 0x04ec, 0x0af0, 0x08f4, 0x0ef8, 0x0cfc, 0x0300, 0x0104, 0x0708, 0x050c, 0x0b10, 0x0914, 0x0f18, 0x0d1c, 0x0320, 0x0124, 0x0728, 0x052c, 0x0b30, 0x0934, 0x0f38, 0x0d3c, 0x0340, 0x0144, 0x0748, 0x054c, 0x0b50, 0x0954, 0x0f58, 0x0d5c, 0x0360, 0x0164, 0x0768, 0x056c, 0x0b70, 0x0974, 0x0f78, 0x0d7c, 0x0380, 0x0184, 0x0788, 0x058c, 0x0b90, 0x0994, 0x0f98, 0x0d9c, 0x03a0, 0x01a4, 0x07a8, 0x05ac, 0x0bb0, 0x09b4, 0x0fb8, 0x0dbc, 0x03c0, 0x01c4, 0x07c8, 0x05cc, 0x0bd0, 0x09d4, 0x0fd8, 0x0ddc, 0x03e0, 0x01e4, 0x07e8, 0x05ec, 0x0bf0, 0x09f4, 0x0ff8, 0x0dfc, }; /* * Output code to buffer. */ int put_code(code, ent, bfr) int code, ent; onebyt *bfr; { int lo_byte; register long mycode; int bits; register onebyt *cp; /* opt: points to bfr[s_at_byte] */ bits = bit_tab[(ent >> 8)]; if (((s_at_bit + bits + 7) >> 3) + s_at_byte > BLKSIZ) { return(-1); } mycode = (long)(code & mask[bits]); #ifdef DEBUG2 fprintf(stderr,"Byte: %d, %lx\n", s_at_byte, mycode); #endif /* lo_byte = bfr[s_at_byte] & mask[s_at_bit];*/ cp = bfr + s_at_byte; /* - */ lo_byte = *cp & mask[s_at_bit]; /* - */ if (s_at_bit != 0) { mycode <<= s_at_bit; } /* bfr[s_at_byte++] = (onebyt) lo_byte | (onebyt) (mycode & 0xff);*/ *cp = (onebyt) lo_byte | (onebyt) (mycode & 0xff); /* - */ s_at_byte++, cp++; /* bfr[s_at_byte] = (onebyt) ((mycode >>= 8) & 0xff);*/ *cp = (onebyt) ((mycode >>= 8) & 0xff); /* - */ if ((s_at_bit += bits) >= 16) { /* bfr[++s_at_byte] = (char)((mycode >>= 8) & 0xff);*/ cp++, s_at_byte++; /* - */ *cp = (onebyt) ((mycode >>= 8) & 0xff); /* - */ } s_at_bit &= 0x07; return(0); } /* * Try LZW compression on the buffer. * * Compresses from "buffer" to "outbuf". "inlen" is the #of bytes of data in * "buffer." Returns the #of bytes of data placed into "outbuf", or -1 on * failure. */ int do_LZW(bufr, inlen, outbuf) onebyt *bufr; int inlen; onebyt *outbuf; { register int index; register onebyt k; register onebyt *inbuf, *endbuf; register struct ht *htp; /* opt: htab[index] */ int ent, prefix, hashdelta; s_at_byte = 0; s_at_bit =0; ent = 0x101; inbuf = bufr; endbuf = bufr + inlen; k = ((char)*inbuf++); Loop0: prefix = (int)k; Loop1: if (inbuf >= endbuf) { if (put_code(prefix, ent, outbuf) < 0) { return(BLKSIZ+2); } if (s_at_bit == 0) return(s_at_byte); else return(s_at_byte+1); } k = (onebyt)*inbuf++; #ifdef OLD_HASH index = (prefix + (k<<4)) & 0xfff; #else index = prefix ^ hashf[k]; /* note index always < 4096 */ #endif Check_ent: htp = htab + index; if (htp->entry == 0) { /* Entry is 0... */ if (put_code(prefix, ent, outbuf) < 0) { return(-1); /* failed */ } htp->entry = ent++; htp->prefix = prefix; htp->chr = k; goto Loop0; } else if (htp->prefix == prefix) { /* - */ /* Same prefix... */ if (htp->chr == k) { /* It's HERE! Yeah! */ prefix = htp->entry; goto Loop1; } goto Sec_hash; } /* Check for more...secondary hash on index */ else { Sec_hash: #ifdef OLD_HASH /*index = (index + (unsigned int)(k) + 1) % HSIZE;*/ index += (unsigned int)(k) +1; if (index >= HSIZE) index -= HSIZE; #else hashdelta = (0x120 - k) << 2; if (index >= hashdelta) index -= hashdelta; else index += (HSIZE - hashdelta); #endif #ifdef DEBUG2 fprintf(stderr,"New ind: %d, k=%d\n", index, (unsigned int)k); #endif goto Check_ent; } } /* * Clear out the hash table. */ void ClearTab() { register int i; register struct ht *htp; /* opt: htab[i] */ /* for(i=0; i < HSIZE; i++)*/ /* htab[i].entry = 0;*/ htp = htab; /* - */ for (i = HSIZE; i; htp++, i--) /* - */ htp->entry = 0; /* - */ } /* * Do run-length encoding * * Takes input from srcptr, and writes to dstptr. Maximum expansion is * (BLKSIZ / 2) + (BLKSIZ / 2) * 3 == 2 * BLKSIZ * Output of form char count where count is #of bytes -1. * * This really isn't very pretty, but it works. */ int do_RLE(srcptr, dstptr) register onebyt *srcptr, *dstptr; { #define ALT_RLE /* testing */ #ifndef ALT_RLE register int found, scount /*, dcount*/; register onebyt c, lastc, tlastc; onebyt *dststart = dstptr; c = *(srcptr++); scount = 1; /*dcount = 0;*/ found = 1; /* one char has been found */ lastc = '\0'; while (scount < BLKSIZ) { tlastc = lastc; lastc = c; c = *(srcptr++); scount++; if (found == 1) { /* no run found */ if (c != lastc) { /* no run starting */ if (lastc == ESCAPE_CHAR) { *(dstptr++) = ESCAPE_CHAR; /*dcount++;*/ *(dstptr++) = lastc; /*dcount++;*/ *(dstptr++) = 0; /*dcount++;*/ /* found one */ } else { *(dstptr++) = lastc; /*dcount++;*/ } found = 1; } else { found = 2; /* they matched, so two in a row */ } } else if (found == 2) { /* got two, try for three */ if (c != lastc) { /* only got two in a row */ if (lastc == ESCAPE_CHAR) { /* and tlastc as well */ *(dstptr++) = ESCAPE_CHAR; /*dcount++;*/ *(dstptr++) = lastc; /*dcount++;*/ *(dstptr++) = 1; /*dcount++;*/ /* found two */ } else { *(dstptr++) = tlastc; /*dcount++;*/ *(dstptr++) = lastc; /*dcount++;*/ } found = 1; } else { /* found 3, got a run going */ found = 3; } } else { /* (found >= 3), got a run going */ if (c == lastc) { /* found another */ found++; } if ((c != lastc) || (found > 256)) { /* end, or too many */ *(dstptr++) = ESCAPE_CHAR; /*dcount++;*/ *(dstptr++) = lastc; /*dcount++;*/ *(dstptr++) = (found > 256) ? 255 : found-1; /*dcount++;*/ found = 1; /* c has something other than the run char */ /* or found is 257-256 = 1 */ } } } /* while */ /* reached end of buffer; flush what was left */ if (found == 1) { if (c == ESCAPE_CHAR) { *(dstptr++) = ESCAPE_CHAR; /*dcount++;*/ *(dstptr++) = c; /*dcount++;*/ *(dstptr++) = 0; /*dcount++;*/ } else { *(dstptr++) = c; /*dcount++;*/ } } else if (found == 2) { /* maybe have if lastc == c == ESCAPE_CHAR? */ if (lastc == ESCAPE_CHAR) { *(dstptr++) = ESCAPE_CHAR; /*dcount++;*/ *(dstptr++) = lastc; /*dcount++;*/ *(dstptr++) = 0; /*dcount++;*/ } else { *(dstptr++) = lastc; /*dcount++;*/ } if (c == ESCAPE_CHAR) { *(dstptr++) = ESCAPE_CHAR; /*dcount++;*/ *(dstptr++) = c; /*dcount++;*/ *(dstptr++) = 0; /*dcount++;*/ } else { *(dstptr++) = c; /*dcount++;*/ } } else { /* found >= 3, in the middle of processing a run */ *(dstptr++) = ESCAPE_CHAR; /*dcount++;*/ *(dstptr++) = c; /*dcount++;*/ *(dstptr++) = found-1; /*dcount++;*/ } /* return (dcount);*/ return (dstptr - dststart); #else /*ALT_RLE*/ /* * This was an attempt to write a faster do_RLE routine. However, * the profiler on my machine showed it to be slower than the big * wad of stuff above. I decided to leave the code here in case somebody * wants to play with it. */ register onebyt *scanptr; onebyt *endptr, *dststart; register onebyt c; register int i, count; endptr = srcptr + BLKSIZ; /* where the source ends */ dststart = dstptr; /* where the destination begins */ while (srcptr < endptr) { c = *srcptr; scanptr = srcptr+1; count = 1; while (*scanptr == c && scanptr < endptr) scanptr++, count++; if (count > 3) { i = count; do { *(dstptr++) = ESCAPE_CHAR; *(dstptr++) = c; if (i > 256) { /* was count */ *(dstptr++) = 255; i -= 256; } else { *(dstptr++) = i-1; /* was count-1 */ break; } } while (i > 0); } else { if (c == ESCAPE_CHAR) { /* special case: 1-3 0xDBs */ *(dstptr++) = ESCAPE_CHAR; *(dstptr++) = ESCAPE_CHAR; *(dstptr++) = count-1; /* count == 0 is legal */ } else { i = count; while (i--) *(dstptr++) = c; } } srcptr += count; } #ifdef DEBUG if (srcptr > endptr) printf("BUG: srcptr > endptr in do_RLE\n"); #endif return (dstptr - dststart); #endif /*ALT_RLE*/ } /* * Main compression entry point. * * Returns actual thread_format used. * * Note that "copybuf" should be at least twice as big as BLKSIZ. */ long pak_SHK(srcfd, dstfd, length, copybuf) int srcfd, dstfd; long length; /* uncompressed size */ onebyt *copybuf; { unsigned int partial; /* size for partial read/write */ onebyt *rptr, *out_buf; register int idx; onebyt scratch[8]; long srcposn, /* start in source file */ startposn, /* start in dest file */ endposn; /* end in dest file */ long unc_len = length, comp_len = 0L; twobyt CRC; int rlesize, lzwsize, out_size; /* length after compression */ int sc; /* spin counter */ static char *procName = "pak_SHK"; if (htab == NULL) htab = (struct ht *) Malloc(HSIZE * sizeof(struct ht)); CRC = 0; if ((srcposn = lseek(srcfd, (off_t) 0, S_REL)) < 0) /* only used if */ Fatal("Bad seek (srcposn)", procName); /* compress fails */ if ((startposn = lseek(dstfd, (off_t) 0, S_REL)) < 0) Fatal("Bad seek (startposn)", procName); lseek(dstfd, (off_t) 4, S_REL); /* leave room for 4-byte header */ comp_len += 4L; sc = 0; do { /* have to handle when length == 0L */ if (length > (long) BLKSIZ) { partial = (unsigned int) BLKSIZ; length -= (long) BLKSIZ; } else { partial = (unsigned int) length; length = 0L; for (idx = partial; idx < BLKSIZ; idx++) /* fill in zeroes */ *(copybuf + idx) = 0; } if (partial > 0) { /* should work anyway, but let's be careful */ if (read(srcfd, copybuf, partial) != partial) Fatal("Source read failed", procName); } /* calc CRC on all 4096 bytes */ CRC = CalcCRC(CRC, (onebyt *) copybuf, BLKSIZ); rlesize = do_RLE(copybuf, copybuf + BLKSIZ+1); /* pack 4096 bytes */ if (rlesize < 0x1000) { /* did it pack or expand? */ rptr = copybuf + BLKSIZ+1; /* use packed version */ } else { rlesize = 0x1000; /* just store original */ rptr = copybuf; } ClearTab(); lzwsize = do_LZW(rptr, rlesize, lbuf); /* compress from rle to lzw */ if ((lzwsize > rlesize) || (lzwsize < 0)) { /* lzw failed, use rle'd copy */ scratch[2] = 0; out_size = rlesize; out_buf = rptr; } else { /* lzw succeeded, use it */ scratch[2] = 1; /* LZW on */ out_size = lzwsize; out_buf = lbuf; } scratch[0] = (onebyt) (rlesize & 0x00ff); /* NOT out_size */ scratch[1] = (onebyt) ((rlesize >> 8) & 0x00ff); if (write(dstfd, scratch, 3) != 3) Fatal("Dest hdr write failed", procName); comp_len += 3; comp_len += out_size; if (comp_len > unc_len) goto bad_compress; /* you didn't see this */ if (write(dstfd, out_buf, out_size) != out_size) /* need to do CRLF */ Fatal("Dest write failed", procName); sc++; if (sc == 15) { sc = 0; Spin(); } } while (length != 0L); if ((endposn = lseek(dstfd, (off_t) 0, S_REL)) < 0) Fatal("Bad seek (now)", procName); if (lseek(dstfd, (off_t) startposn, S_ABS) < 0) Fatal("Bad seek (to4)", procName); scratch[0] = (char) CRC; scratch[1] = (char) (CRC >> 8); scratch[2] = 0; scratch[3] = ESCAPE_CHAR; if (write(dstfd, scratch, 4) != 4) Fatal("Dest hdr write failed", procName); if (lseek(dstfd, (off_t) endposn, S_ABS) < 0) Fatal("Bad seek (last)", procName); if (comp_len != endposn - startposn) { printf( "internal error: comp_len=%ld, endposn=%ld, startposn=%ld (%ld)\n", comp_len, endposn, startposn, endposn - startposn); } packedSize = comp_len; return (0x0002); /* SHK packing */ bad_compress: /* I'm too lazy to do a procedure call... */ if (verbose) { printf("storing..."); fflush(stdout); } if (lseek(srcfd, (off_t) srcposn, S_ABS) < 0) Fatal("Bad seek (srcposn in bad_compress)", procName); if (lseek(dstfd, (off_t) startposn, S_ABS) < 0) Fatal("Bad seek (startposn in bad_compress)", procName); FCopy(srcfd, dstfd, unc_len, copybuf, FALSE); packedSize = unc_len; return (0x0000); /* no compression */ } /* * P8 ShrinkIt uncompression routines * * Copyright 1989 Kent Dickey * C translation by Kent Dickey / Andy McFadden * Modifications for LZW-II designed by Andy Nicholas * * C decoder for LZW-II by Frank Petroski / Kent Dickey (simultaneously * and independently). Speed optimizations by Kent Dickey. */ /*static int inf; /* to make Getc() calls happy */ static BOOLEAN type2; /* true if working with LZW-II format */ static onebyt escape_char; typedef struct { unsigned char chr; int prefix; } Table_ent; static Table_ent Real_tab[BLKSIZ-256]; /* first 256 don't exist */ static Table_ent *Table; static int Mask_tab[17] = { 0x0000, 0x01ff, 0x03ff, 0x03ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff }; static int Number[17] = { /* 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,4 };*/ 8,9,10,10,11,11,11,11,12,12,12,12,12,12,12,12,12 }; static onebyt *Stack; /* simulated stack <= 64 for LZW-I, <= 4096 for II */ static int out_bytes, stack_ptr, entry, at_bit, at_byte; static onebyt last_byte; /* used in get_code */ static int reset_fix; /* fix problem unpacking certain LZW-II archives */ /* fake getc() */ # define Getc() *(ibuf++) /* * Stack operations; used by undo_LZW and undo_LZW_2 */ #ifdef DEBUG # define push(a_byte) \ { \ if (stack_ptr - Stack > 4096) { \ printf("\n*** stack_ptr exceeded 4096 in push() [%d]\n", \ (int) (stack_ptr - Stack));\ exit (-1); \ } \ *(stack_ptr++) = (onebyt) a_byte; \ } #else # define push(a_byte) *(stack_ptr++) = (onebyt) a_byte; #endif #ifdef DEBUG # define dump_stack(buffer) \ { \ printf("--- Going to dump stack, stack_ptr = %d, out_bytes = %d\n", \ (int) (stack_ptr - Stack), out_bytes); \ while (stack_ptr-- > stack_start) { \ *(buffer++) = *stack_ptr; \ } \ } #else # define dump_stack(buffer) \ while (stack_ptr-- > stack_start) { \ *(buffer++) = *stack_ptr; \ } #endif /* * Decipher LZW codes. */ int get_code() { register unsigned int num_bits, old_bit, last_bit; long value, mask; /* onebyt byte1, byte2, byte3;*/ /* get compressed chars... */ long byte1, byte2, byte3; /* - */ #ifdef DEBUG printf("ENT: bit=%d byte=%-4d last_byte=$%.2x ", at_bit, at_byte, last_byte); printf("Entry: %.4x \n", entry); #endif num_bits = ((entry+1) >> 8); /* get hi-byte of entry */ /* last_bit = at_bit + Number[num_bits] + 8;*/ last_bit = at_bit + Number[num_bits]; old_bit = at_bit; #ifdef DEBUG if (at_byte >= BLKSIZ) { fprintf(stderr, "at_byte exceeded BLKSIZ (4096) in get_code()\n"); exit (-1); } #endif if (at_bit == 0) last_byte = Getc(); /* byte1 = last_byte;*/ /* first byte = last one used */ byte1 = (long) last_byte; /* - */ last_byte = Getc(); /* - */ /* byte2 = Getc(inf);*/ byte2 = ((long) last_byte) << 8; /* - */ if (last_bit > 16) { /* get 3rd byte if nec. */ /* byte3 = Getc(inf);*/ /* last_byte = byte3;*/ last_byte = Getc(); /* - */ byte3 = ((long) last_byte) << 16; /* - */ } else { byte3 = 0; /* last_byte = byte2;*/ } /* value = ((((long)byte3 << 8) + (long)byte2) << 8) + (long)byte1;*/ value = byte3 + byte2 + byte1; /* - */ mask = (long) Mask_tab[num_bits]; at_byte += (last_bit >> 3); /* new byte */ at_bit = (last_bit & 0x07); #ifdef DEBUG printf("| EX: value=$%.6x mask=$%.4x return=$%.3x\n", value, mask, ((value >> old_bit) & mask)); #endif #ifdef ZERO_SHIFT_BAD if (old_bit) return ((value >> old_bit) & mask); else return (value & mask); /* shifting by zero may be undefined */ #else return ((value >> old_bit) & mask); #endif /*ZERO_SHIFT_BAD*/ } /* * Un-LZW a range of bytes * * Reads data with get_code() and stores the output in "buffer". */ void undo_LZW(buffer, length) unsigned char *buffer; /* where to put output */ int length; /* uncompressed length of output */ { register int oldc, incode, finalc, ptr; register onebyt *endbuf, *stack_ptr, *stack_start; /* initialize variables */ Table = Real_tab-256; entry = 0x101; /* start at $101 */ at_bit = at_byte = 0; endbuf = buffer + length; stack_start = stack_ptr = Stack; last_byte = 0; /* init last_byte */ oldc = incode = get_code(/*buffer*/); finalc = (oldc & 0xff); *(buffer++) = (onebyt) incode; /* main loop */ while (buffer < endbuf) { incode = ptr = get_code(/*buffer*/); if (ptr >= entry) { /* handle KwKwK case */ push(finalc); ptr = oldc; } while (ptr > 0xff) { /* fill the stack */ push(Table[ptr].chr); ptr = Table[ptr].prefix; } /* ptr is now < 0x100 */ finalc = ptr; *(buffer++) = (onebyt) finalc; while (stack_ptr > stack_start) /* dump the stack */ *(buffer++) = *(--stack_ptr); Table[entry].chr = (finalc & 0xff); /* mask to get unsigned?? byte */ Table[entry].prefix = oldc; entry++; oldc = incode; } } /* * Un-LZW-II a range of bytes * * Reads data with get_code() and stores the output in "buffer". Has * additional code to support LZW-II's table clears. */ void undo_LZW_2(buffer, length) unsigned char *buffer; /* where to put output */ int length; /* uncompressed length of output */ { register int oldc, incode, finalc, ptr; register onebyt *endbuf, *stack_ptr, *stack_start; /* initialize variables */ at_bit = at_byte = 0; /* out_bytes = 0;*/ /* stack_ptr = 0;*/ endbuf = buffer + length; stack_start = stack_ptr = Stack; last_byte = 0; /* init last_byte */ /* main loop */ /* while (out_bytes < length) {*/ while (buffer < endbuf) { /* - */ if (entry == 0x101 && !reset_fix) { /* table is really empty */ oldc = incode = get_code(/*buffer*/); finalc = (oldc & 0xff); /* *(buffer + out_bytes++) = (unsigned char) incode;*/ *(buffer++) = (onebyt) incode; /* - */ if (buffer >= endbuf) { /* buffer is full? If so, we */ reset_fix = 1; /* want to skip get_code() next time */ break; /* we come through for next chunk */ } } incode = ptr = get_code(/*buffer*/); if (incode == 0x100) { /* Clear table code */ entry = 0x101; /* start at $101 */ Table = Real_tab-256; reset_fix = 0; continue; } if (ptr >= entry) { push(finalc); ptr = oldc; } while (ptr > 0xff) { push(Table[ptr].chr); ptr = Table[ptr].prefix; } /* ptr is now < 0x100 */ finalc = ptr; /* push(finalc);*/ /* dump_stack(buffer);*/ *(buffer++) = (onebyt) finalc; /* - */ while (stack_ptr > stack_start) /* - */ *(buffer++) = *(--stack_ptr); /* - */ Table[entry].chr = (finalc & 0xff); /* mask to get unsigned?? byte */ Table[entry].prefix = oldc; entry++; oldc = incode; } } /* * Second pass... undo the Run Length Encoding. * * Copy data from inbuffer to outbuffer. Keep going until we've got * exactly BLKSIZ bytes. Note that this uses codes of the form * char count * which is different from some other programs. */ void undo_RLE(inbuffer, outbuffer) unsigned char *inbuffer, *outbuffer; /*int length; /* how many bytes from LZW; just to make sure... */ { register onebyt c; register int count; /* count is RLE reps */ register onebyt *outbufend; #ifdef DEBUG /*printf("Starting undo_RLE, length = %d\n", length);*/ #endif outbufend = outbuffer + BLKSIZ; while (outbuffer < outbufend) { c = *(inbuffer++); /*length--;*/ if (c == (onebyt) escape_char) { c = *(inbuffer++); /*length--;*/ count = *(inbuffer++); /*length--;*/ while (count-- >= 0) { *(outbuffer++) = c; /*Putc(c, outf);*/ } } else { *(outbuffer++) = c; /*Putc(c, outf);*/ } } if (outbuffer != outbufend) fprintf(stderr, "internal error: bad undo_RLE\n"); #ifdef DEBUG /* printf("Exiting undo_RLE, length = %d (should be 0), total = %d (4096)\n", length, outbufend-outbuffer);*/ #endif } /* * Main entry point. * * This is among the more hellish things I've written. Uses * a large buffer for efficiency reasons, and unpacks a stream of bytes. * * If you find this hard to understand, imagine what it was like to debug. */ void unpak_SHK(srcfd,dstfd,comp_thread_eof,thread_eof,buffer, use_type2, thread_crc) int srcfd, dstfd; fourbyt comp_thread_eof, thread_eof; register onebyt *buffer; BOOLEAN use_type2; /* true if we should expect LZW-II */ twobyt thread_crc; { twobyt CRC, blkCRC; onebyt vol; onebyt *wrbuf; /* points to buffer we're about to write */ short unlen, lzwflag, rleflag, complen; /* should be short */ unsigned int partial, toread, still_in_buf /*, crcsize*/; fourbyt tmp4; /* temporary 4-byte variable */ int cc; static char *procName = "unpak_SHK"; if (Stack == NULL) Stack = (onebyt *) Malloc(4096); type2 = use_type2; if (type2) CRC = 0xffff; /* different CRC for LZW-II */ else CRC = 0; /* initialize variables for LZW-II */ Table = Real_tab-256; entry = 0x101; /* start at $101 */ reset_fix = 0; /* read min(PAKBUFSIZ, comp_thread_eof) bytes into buffer */ if (comp_thread_eof > (fourbyt) PAKBUFSIZ) { toread = (unsigned int) PAKBUFSIZ; comp_thread_eof -= (fourbyt) PAKBUFSIZ; } else { toread = (unsigned int) comp_thread_eof; /* read it all... */ comp_thread_eof = (fourbyt) 0; } /* do initial read */ #ifdef DEBUG1 printf("initial read = %u\n", toread); #endif if ((cc = read(srcfd, buffer, toread)) < toread) { #ifdef DEBUG1 printf("Only read %d bytes\n", cc); #endif Fatal("Bad read during uncompress", procName); } ibuf = buffer; /* set input pointer to start of buffer */ /* get header data */ if (type2) { blkCRC = thread_crc; } else { blkCRC = Getc(); blkCRC += (Getc() << 8); } vol = (char) Getc(); /* disk volume #; not used here */ escape_char = (char) Getc(); /* RLE delimiter */ #ifdef DEBUG1 printf("vol = %d, escape_char = %x\n", vol, escape_char); #endif /* * main loop */ while (thread_eof != (fourbyt) 0) { /* note "unlen" is un-LZWed length (i.e., after RLE) */ if (type2) { unlen = Getc(); unlen += (Getc() << 8); lzwflag = (unlen & 0x8000) ? 1 : 0; /* flag is hi bit */ unlen &= 0x1fff; /* strip extra stuff */ rleflag = (unlen != BLKSIZ); if (lzwflag) { /* will the real length bytes please stand up*/ complen = Getc(); complen += (Getc() << 8); } } else { unlen = Getc(); unlen += (Getc() << 8); lzwflag = Getc(); rleflag = (unlen != BLKSIZ); } #ifdef DEBUG1 printf("Length after RLE = %d ($%.4x)\n", unlen, unlen); printf("LZW flag = %d, RLE flag = %d\n", lzwflag, rleflag); if (lzwflag != 0 && lzwflag != 1) { /* this is weird... */ for (lzwflag = -6; lzwflag < 3; lzwflag++) { printf("foo %d: %.2x\n", lzwflag, *(ibuf+lzwflag)); } } if (type2 && lzwflag) { printf("Length after RLE+LZW = %d ($%.4x)\n", complen, complen); } #endif /* If it looks like we're going to run out of room, shift & read /* Mostly a guess; LZW length is less than unlen... This is /* complicated and very prone to errors (but we err on the safe side). /* tmp4 is the number of bytes between the current ptr and the end; /* some (16-bit) compilers yack if it's all one statement.*/ tmp4 = (fourbyt) buffer + (fourbyt) PAKBUFSIZ; tmp4 -= (fourbyt) ibuf; if (tmp4 < (unlen + 6)) { /* 6 = 3/4 byte header + two just in case */ still_in_buf = tmp4; #ifdef DEBUG1 printf("--- unlen = %d, space left = %d bytes\n", unlen, still_in_buf); #endif /* BCopy((onebyt *) ibuf, (onebyt *) buffer, still_in_buf, FALSE);*/ BCOPY((onebyt *) ibuf, (onebyt *) buffer, still_in_buf); if (comp_thread_eof != (fourbyt) 0) { /* no read, just shift */ if (comp_thread_eof > ((fourbyt) PAKBUFSIZ - still_in_buf)){ toread = (unsigned int) PAKBUFSIZ - still_in_buf; comp_thread_eof -= (fourbyt) PAKBUFSIZ - still_in_buf; } else { toread = (unsigned int) comp_thread_eof; comp_thread_eof = (fourbyt) 0; } #ifdef DEBUG1 printf("--- reading another %u bytes\n", toread); #endif if (read(srcfd, buffer+still_in_buf, toread) < toread) Fatal("Unable to read [middle]", procName); if (verbose) Spin(); } ibuf = buffer; } /* how much of the buffered data do we really need? */ if (thread_eof > (fourbyt) BLKSIZ) { partial = (unsigned int) BLKSIZ; thread_eof -= (fourbyt) BLKSIZ; } else { partial = (unsigned int) thread_eof; /* last block of file */ thread_eof = (fourbyt) 0; } /* * undo_LZW reads from ibuf (using Getc()) and writes to lbuf * undo_LZW_2 does what undo_LZW does, but for LZW-II. * undo_RLE reads from where you tell it and writes to rbuf * * This is really insane... */ if (lzwflag && rleflag) { if (type2) undo_LZW_2(lbuf, unlen); /* from ibuf -> lbuf */ else undo_LZW(lbuf, unlen); /* from ibuf -> lbuf */ undo_RLE(lbuf, rbuf); /* from lbuf -> rbuf */ wrbuf = rbuf; /* write rbuf */ } else if (lzwflag && !rleflag) { if (type2) undo_LZW_2(lbuf, unlen); /* from ibuf -> lbuf */ else undo_LZW(lbuf, unlen); /* from ibuf -> lbuf */ wrbuf = lbuf; /* write lbuf */ } else if (!lzwflag && rleflag) { undo_RLE(ibuf, rbuf); /* from ibuf -> rbuf */ wrbuf = rbuf; /* write rbuf */ ibuf += unlen; /* have to skip over RLE-only data */ /* normally ibuf is advanced by Getc() calls */ Table = Real_tab-256; /* must clear table if no LZW */ entry = 0x101; /* start at $101 */ reset_fix = 0; } else { wrbuf = ibuf; /* write ibuf */ ibuf += partial; /* skip over uncompressed data */ /* normally ibuf is advanced by Getc() calls */ Table = Real_tab-256; /* must clear table if no LZW */ entry = 0x101; /* start at $101 */ reset_fix = 0; } if (type2) CRC = CalcCRC(CRC, wrbuf, partial); else CRC = CalcCRC(CRC, wrbuf, BLKSIZ); #ifdef DEBUG1 printf("Writing %d bytes.\n", partial); #endif if (crlf(dstfd, wrbuf, partial) < partial) /* write wrbuf */ Fatal("Bad write", procName); } if (CRC != blkCRC) { fprintf(stderr, "WARNING: CRC does not match..."); if (verbose) fprintf(stderr, "CRC is %.4x vs %.4x\n", CRC, blkCRC); else fprintf(stderr, "extract with V suboption to see filenames.\n"); } } eflag = (unlen != BLKSIZ); if (lzwflag) { /* will the real length bytes please stand up*/ complen = Getc(); complen += (Getc() << 8); } } else { unlen = Getc(); unlen += (Getc() << 8); lzwflag = Getc(); rleflag = (unlen != BLKSIZ); } #ifdef DEBUG1 printf("Length afnulib/nusq.c 644 4170 1464 16464 5253231464 6502 /* * nusq.c - Huffman squeeze/unsqueeze routines * * NuLib v3.2 March 1992 Freeware (distribute, don't sell) * By Andy McFadden (fadden@uts.amdahl.com) */ #ifdef APW segment "Compress" #endif #include "nudefs.h" #include #include #ifdef UNIX # include #endif #ifdef MSDOS /* For file IO */ # include # include # include # include #endif #include "nuetc.h" /* * usq.c - undo Huffman coding * Adapated from code By Marcel J.E. Mol * Based on sq3/usq2 by Don Elton * * Squeezed file format: * 2 bytes MAGIC * 2 bytes dummy ??? (maybe CRC or checksum; not checked) * filename ended by \0 * * 2 bytes node count * node count node values, each 2 bytes * squeezed data per byte * * NuFX SQueezed format includes only the node count, node values, and * the data. The BLU routines are expected to strip off the MAGIC, * checksum, and filename before calling this. */ /*char *copyright = "@(#) usq.c 2.1 18/06/88 (c) M.J.E. Mol";*/ #define BUFSIZE 128 #define MAGIC 0xff76 /* Squeezed file magic */ #define DLE 0x90 /* repeat byte flag */ #define NOHIST 0 /* no relevant history */ #define INREP 1 /* sending a repeated value */ #define SPEOF 256 /* special endfile token */ #define NUMVALS 257 /* 256 data values plus SPEOF */ /* global variable declarations */ char *sfn; /* squeezed file name */ struct nd { /* decoding tree */ int child[2]; /* left, right */ } node[NUMVALS]; /* use large buffer */ int state; /* repeat unpacking state */ int bpos; /* last bit position read */ int curin; /* last byte value read */ int numnodes; /* number of nodes in decode tree */ static unsigned char fromc; /* for use in text translation */ static BOOLEAN trbool; /* BOOLEAN version of transfrom */ /* Get an integer from the input stream */ static twobyt get_int(f) FILE *f; { twobyt val; val = (twobyt)getc(f); val += (twobyt)getc(f) << 8; return (val); } static int getc_usq(f) /* get byte from squeezed file */ FILE *f; /* file containing squeezed data */ { register short i; /* tree index */ /* follow bit stream in tree to a leaf */ for (i=0; (i <= 0x7fff) && (i>=0); )/* work down(up?) from root */ { if (++bpos > 7) { if ((curin=getc(f)) == EOF) return(EOF); bpos = 0; /* move a level deeper in tree */ i = node[i].child[1 & curin]; } else i = node[i].child[1 & (curin >>= 1)]; } /* decode fake node index to original data value */ i = -(i + 1); /* decode special endfile token to normal EOF */ return ((i==SPEOF) ? EOF : i); } /* putc-ncr -- decode non-repeat compression. Bytes are passed one * at a time in coded format, and are written out uncoded. * The data is stored normally, except that runs of more * than two characters are represented as: * * * * With a special case that a count of zero indicates a DLE * as data, not as a repeat marker. */ static void putc_ncr(c, t) /* put NCR coded bytes */ unsigned char c; /* next byte of stream */ FILE *t; /* file to receive data */ { static int lastc; /* last character seen */ /* if converting line terminators, do so now */ if (trbool && (c == fromc)) #ifdef UNIX c = 0x0a; #else # ifdef APW c = 0x0d; # else c = 0x0d; /* No CRLF stuff in unSQueeze... sorry */ # endif #endif switch (state) { /* action depends on our state */ case NOHIST: /* no previous history */ if (c==DLE) /* if starting a series */ state = INREP; /* then remember it next time */ else putc(lastc=c, t); /* else nothing unusual */ return; case INREP: /* in a repeat */ if (c) /* if count is nonzero */ while (--c) /* then repeatedly ... */ putc(lastc, t); /* ... output the byte */ else putc(DLE, t); /* else output DLE as data */ state = NOHIST; /* back to no history */ return; default: fprintf(stderr, "%s: bad NCR unpacking state (%d)", prgName, state); } } static int init_usq(f) /* initialize Huffman unsqueezing */ FILE *f; /* file containing squeezed data */ { register int i; /* node index */ switch (transfrom) { case -1: /* no translation */ trbool = 0; break; case 0: /* from ProDOS */ trbool = 1; fromc = 0x0d; break; case 1: /* from UNIX */ trbool = 1; fromc = 0x0a; break; case 2: /* from MS-DOS... this needs fixing */ trbool = 1; fromc = 0x0a; /* just turn LFs into whatever... */ break; default: /* unknown */ fprintf(stderr, "%s: unknown translation type %d\n", prgName, trbool); fprintf(stderr, "%s: assuming conversion from CR\n", prgName); trbool = 1; /* should just ignore flag, but other procs do this */ fromc = 0x0d; break; } bpos = 99; /* force initial read */ numnodes = get_int(f); /* get number of nodes */ if (numnodes<0 || numnodes>=NUMVALS) { fprintf(stderr, "%s: usq: archived file has invalid decode tree\n", prgName); return (-1); } /* initialize for possible empty tree (SPEOF only) */ node[0].child[0] = -(SPEOF + 1); node[0].child[1] = -(SPEOF + 1); for (i=0; i #ifdef BSD43 # include #else /* SYSV, APW, MSC */ # include #endif #ifdef APW # include #endif #include "nuview.h" #include "nuread.h" #include "nuetc.h" /* * String definitions for NuView */ /* unknown value msg */ char *unknownStr = "[ unknown ]"; /* weekDay values */ char *WD[8] = { "[ null ]", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; /* month values */ char *MO[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* thread_class */ /*#define TCn 4*/ char *TC[TCn] = { "Message_thread", "Control_thread", "Data_thread", "Filename_thread" }; /*#define TKn 3 /* max #of thread_kinds in a thread_class */ char *TK[TCn][TKn] = { { "ASCII text", "ASCII text (predef size)", "" }, { "Create directory", "", "" }, { "File data_fork", "Disk image", "File resource_fork" }, { "Generic filename", "", "" } }; /* thread_format */ /*#define TFn 6*/ char *TF[TFn] = { "Uncompressed", "SQueezed (SQ/USQ)", "Dynamic LZW Type I (ShrinkIt)", "Dynamic LZW Type II (ShrinkIt)", "12-bit UNIX compress", "16-bit UNIX compress" }; /* brief thread format */ /*#define BTFn 6*/ char *BTF[BTFn] = { "Uncompr", "SQueezed", "LZW/1", "LZW/2", "Unix/12", "Unix/16" }; /* quick thread_format */ /*#define QTFn 6*/ char *QTF[QTFn] = { "unc", "squ", "shk", "sh2", "u12", "u16" }; /* file_sys_id */ /*#define FIDn 14*/ char *FID[FIDn] = { "Reserved/unknown ($00)", "ProDOS/SOS", "DOS 3.3", "DOS 3.2", "Apple II Pascal", "Macintosh (MFS)", "Macintosh (HFS)", "LISA file system", "Apple CP/M", "Reserved ($09)", "MS-DOS", "High-Sierra", "ISO 9660", "AppleShare" }; /* storage_type */ /*#define STn 14*/ char *ST[STn] = { "Standard file ($00)", "Standard file ($01)", "Standard file ($02)", "Standard file ($03)", "??? ($04)", "Extended file ($05)", "??? ($06)", "??? ($07)", "??? ($08)", "??? ($09)", "??? ($0a)", "??? ($0b)", "??? ($0c)", "Subdirectory ($0d)" }; /* file type names */ char *FT[256] = { "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", "FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", "TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", "$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", "DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", "$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", "$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", "$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", "$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", "$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", "$88", "$89", "$8A", "$8B", "$8C", "$8D", "$8E", "$8F", "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", "$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", "WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", "NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", "$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", "SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS", "CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7", "$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS" }; /* * NuView program */ /* print date from Time structure */ char * PrintDate(tptr, brief) Time *tptr; int brief; { static char buf[64]; /* holds final date string; must be static */ char buf2[64]; /* space to hold string while building it */ /* check for validity */ if ( (tptr->day > 30) || (tptr->month > 11) || (tptr->hour > 24) || (tptr->minute > 59) ) { strcpy(buf, " "); return (buf); } if (!tptr->second && !tptr->minute && !tptr->hour && !tptr->day && !tptr->month && !tptr->year && !tptr->weekDay && !tptr->extra) { strcpy(buf, " [No Date] "); return (buf); } /* only print weekDay if one was stored and if we're in FULL mode */ if (!brief && tptr->weekDay) { (void) sprintf(buf, "%s, ", WD[tptr->weekDay]); } else { buf[0] = '\0'; } if (brief == 2) { /* special case for ARCZOO format */ (void) sprintf(buf2, "%.2d-%s-%.2d %.2d:%.2d%c", (tptr->day)+1, MO[tptr->month], tptr->year, tptr->hour > 12 ? tptr->hour-12 : tptr->hour, tptr->minute, tptr->hour > 12 ? 'p' : 'a'); } else { (void) sprintf(buf2, "%.2d-%s-%.2d %.2d:%.2d", (tptr->day)+1, MO[tptr->month], tptr->year, tptr->hour, tptr->minute); } (void) strcat(buf, buf2); if (!brief) { /* add seconds to long output */ (void) sprintf(buf2, ":%.2d", tptr->second); (void) strcat(buf, buf2); } return (buf); } /* * Dump contents of the threads (used by FULL view mode) */ static void DumpThreads(RNodePtr) RNode *RNodePtr; { int i; fourbyt count = RNodePtr->RHptr->total_threads; static char ind[4] = " "; /* indentation */ THblock *THptr; TNode *TNodePtr; /* go through all threads, printing as we go */ TNodePtr = RNodePtr->TNodePtr; for (i = 0; (fourbyt) i < count; i++) { if (TNodePtr == (TNode *) NULL) { fprintf(stderr, "WARNING: fewer threads than expected\n"); return; } THptr = TNodePtr->THptr; printf("%s --> Information for thread %d\n", ind, i); printf("%s thread_class: %s\n", ind, THptr->thread_class < TCn ? TC[THptr->thread_class] : unknownStr); printf("%s thread_format: %s\n", ind, THptr->thread_format < TFn ? TF[THptr->thread_format] : unknownStr); printf("%s thread_kind: %s ($%.2X)\n", ind, (THptr->thread_kind < TKn && THptr->thread_class < TCn) ? TK[THptr->thread_class][THptr->thread_kind] : unknownStr, THptr->thread_kind); printf("%s thread_crc: $%.4x\n", ind, THptr->thread_crc); printf("%s thread_eof: %lu ", ind, THptr->thread_eof); printf("comp_thread_eof: %lu\n", THptr->comp_thread_eof); printf("%s * position within file: %ld\n", ind, TNodePtr->fileposn); TNodePtr = TNodePtr->TNext; } /* after all info printed, show sum total of thread lengths */ printf("%s * total thread_eof: %lu ", ind, RNodePtr->unc_len); printf("total comp_thread_eof: %lu\n", RNodePtr->comp_len); } /* * Scan contents of the threads for certain things (for PROSHK view mode) * Returns 65535 as error code (-1 in an unsigned short). * Places the format, compressed EOF, and uncompressed EOF in the location * pointed to by the appropriate variables. * * This will probably fail if there are > 32767 threads. */ static twobyt ScanThreads(RNodePtr, format, dataCEOF, dataEOF) RNode *RNodePtr; twobyt *format; /* format of the data_fork thread */ long *dataCEOF; /* length of the data_fork thread (compressed) */ long *dataEOF; /* length of the data_fork thread (uncompressed) */ { int i; int count; THblock *THptr; TNode *TNodePtr; count = (int) RNodePtr->RHptr->total_threads; *format = 65535; /* default = error */ *dataCEOF = 0L; *dataEOF = 0L; TNodePtr = RNodePtr->TNodePtr; for (i = 0; i < count; i++) { if (TNodePtr == (TNode *) NULL) { fprintf(stderr, "WARNING: fewer threads than expected\n"); return (65535); } THptr = TNodePtr->THptr; if (THptr->thread_class == 2) { /* data thread? */ *format = THptr->thread_format; *dataCEOF = THptr->comp_thread_eof; *dataEOF = THptr->thread_eof; return (THptr->thread_kind); } TNodePtr = TNodePtr->TNext; } return (65535); /* no data thread found */ } /* * View archive contents * * Format types: * T: NAMEONLY - Brief output of filenames only (good for pipes) * V: PROSHK - ProDOS ShrinkIt format * A: ARCZOO - Format similar to ARC or ZOO * Z: FULL - Fully detailed output */ void NuView(filename, options) char *filename; char *options; { ListHdr *archive; MHblock *MHptr; RHblock *RHptr; RNode *RNodePtr; outtype prtform; int rec; char tmpbuf[80]; /* temporary buffer for sprintf + printf */ twobyt format, datakind; /* PROSHK */ int percent; /* PROSHK & ARCZOO */ long dataCEOF, dataEOF; /* PROSHK */ long total_files = 0L, total_length = 0L, total_complen = 0L; /* ARCZOO */ #ifdef APW /* kill "not used" messages */ char *ptr; #endif static char *procName = "NuView"; /* process options ourselves */ switch (options[0]) { case 't': if (INDEX(options+1, 'v')) prtform = PROSHK; /* -tv is same as -v */ else if (INDEX(options+1, 'a')) prtform = ARCZOO; else if (INDEX(options+1, 'z')) prtform = FULL; else prtform = NAMEONLY; break; case 'v': prtform = PROSHK; break; default: fprintf(stderr, "%s internal error: unknown output format\n", prgName); Quit (-1); } archive = NuRead(filename); MHptr = archive->MHptr; /* Print master header info */ if (prtform == NAMEONLY) { /* don't print any info from master header for NAMEONLY */ } else if (prtform == PROSHK) { #ifdef APW /* strip partial paths from APW filename (if any) */ ptr = RINDEX(archive->arc_name, '/'); printf(" %-15.15s ", ptr ? ptr+1 : archive->arc_name); #else printf(" %-15.15s ", archive->arc_name); #endif printf("Created:%s ", PrintDate(&MHptr->arc_create_when, TRUE)); printf("Mod:%s ", PrintDate(&MHptr->arc_mod_when, TRUE)); printf("Recs:%5lu\n\n", MHptr->total_records); printf(" Name Kind Typ Auxtyp Archived"); printf(" Fmat Size Un-Length\n"); printf("-------------------------------------------------") ; printf("----------------------------\n"); } else if (prtform == ARCZOO) { printf("Name Length Stowage SF Size now"); printf(" Date Time \n"); printf("======================== ======== ======== ==== ========"); printf(" ========= ======\n"); } else if (prtform == FULL) { printf("Now processing archive '%s'\n", archive->arc_name); printf("---> Master header information:\n"); printf("master ID: '%.6s' ", MHptr->ID); printf("master_version: $%.4x ", MHptr->master_version); printf("master_crc: $%.4X\n", MHptr->master_crc); printf("total_records: %lu ", MHptr->total_records); if (MHptr->master_version >= 0x0001) { printf("master_eof: %lu\n", MHptr->master_eof); } else { printf("\n"); } printf("created: %s ", PrintDate(&MHptr->arc_create_when, FALSE)); printf("mod: %s\n", PrintDate(&MHptr->arc_mod_when, FALSE)); } else { printf("NuView internal error: undefined output format\n"); Quit (-1); } /* Print record info */ RNodePtr = archive->RNodePtr; for (rec = 0; (fourbyt) rec < MHptr->total_records; rec++) { if (RNodePtr == (RNode *) NULL) { fprintf(stderr, "WARNING: fewer records than expected\n"); return; } RHptr = RNodePtr->RHptr; if (prtform == NAMEONLY) { printf("%.79s\n", RNodePtr->filename); /* max 79 chars */ } else if (prtform == PROSHK) { printf("%c", (RHptr->access == 0xE3L || RHptr->access == 0xC3L) ? ' ' : '+'); printf("%-21.21s ", RNodePtr->filename); /* get info on data_fork thread */ datakind = ScanThreads(RNodePtr, &format, &dataCEOF, &dataEOF); if (datakind == 65535) { /* no data thread... */ printf("???? "); printf("%s ", RHptr->file_type < 256L ? FT[RHptr->file_type] : "???"); printf("$%.4X ", (twobyt) RHptr->extra_type); } else if (datakind == 1) { /* disk */ printf("Disk "); printf("--- "); (void) sprintf(tmpbuf, "%dk", (twobyt) RHptr->extra_type / 2); printf("%-5s ", tmpbuf); } else { /* must be a file */ printf("File "); printf("%s ", RHptr->file_type < 256L ? FT[RHptr->file_type] : "???"); printf("$%.4X ", (twobyt) RHptr->extra_type); } printf("%s ", PrintDate(&RHptr->archive_when, TRUE)); printf("%s ", format < QTFn ? QTF[format] : "???"); /* figure out the percent size, and format it appropriately */ /* Note RNodePtr->comp_len corresponds to dataCEOF, and */ /* RNodePtr->unc_len corresponds to dataEOF. */ if (!dataCEOF && !dataEOF) { printf("100%% "); /* file is 0 bytes long */ } else if ((!dataEOF && dataCEOF) || (dataEOF && !dataCEOF)) { printf("--- "); /* something weird happened */ } else if (dataEOF < dataCEOF) { printf(">100%% "); /* compression failed?!? */ } else { /* compute from sum of thread lengths (use only data?) */ percent = (dataCEOF * 100) / dataEOF; (void) sprintf(tmpbuf, "%.2d%%", percent); printf("%-4s ", tmpbuf); } if (!dataEOF && dataCEOF) /* weird */ printf(" ????\n"); else printf("%7ld\n", dataEOF); /* was 8ld */ } else if (prtform == ARCZOO) { printf("%-24.24s ", RNodePtr->filename); datakind = ScanThreads(RNodePtr, &format, &dataCEOF, &dataEOF); printf("%8ld ", dataEOF); printf("%-8.8s ", format < BTFn ? BTF[format] : "Unknown"); /* figure out the percent size, and format it appropriately */ /* Note RNodePtr->comp_len corresponds to dataCEOF, and */ /* RNodePtr->unc_len corresponds to dataEOF. */ if (!dataCEOF && !dataEOF) { printf(" 0%% "); /* file is 0 bytes long */ } else if ((!dataEOF && dataCEOF) || (dataEOF && !dataCEOF)) { printf("--- "); /* something weird happened */ } else if (dataEOF < dataCEOF) { printf(" <0%% "); /* compression failed?!? */ } else { /* compute from sum of thread lengths (use only data?) */ percent = 100 - ((dataCEOF * 100) / dataEOF); if (percent == 0 || percent == 100) (void) sprintf(tmpbuf, "%d%%", percent); else (void) sprintf(tmpbuf, "%.2d%%", percent); printf("%4s ", tmpbuf); } printf("%8ld ", dataCEOF); printf("%s\n", PrintDate(&RHptr->mod_when, 2)); total_files++; total_length += dataEOF; total_complen += dataCEOF; } else if (prtform == FULL) { printf("\n---> Information for record %d:\n", rec); printf("Filename: (%d) '%s'\n", RNodePtr->filename_length, RNodePtr->filename); printf("header ID: '%.4s' ", RHptr->ID); printf("version_number: $%.4X ", RHptr->version_number); printf("header_crc: $%.4X\n", RHptr->header_crc); printf("attrib_count: %u ", RHptr->attrib_count); printf("total_threads: %u\n", RHptr->total_threads); printf("file_sys_id: %s ", RHptr->file_sys_id < FIDn ? FID[RHptr->file_sys_id] : unknownStr); printf("sep: '%c'\n", RHptr->file_sys_info); if (RHptr->file_sys_id == 0x0001) { /* ProDOS-specific */ printf("access: %s ($%.8lX) ", (RHptr->access == 0xE3L || RHptr->access == 0xC3L) ? "Unlocked" : "Locked", RHptr->access); printf("file_type: %s ($%.8lX)\n", RHptr->file_type < 256L ? FT[RHptr->file_type] : "???", RHptr->file_type); } else { /* all other filesystems */ printf("access: $%.8lX ", RHptr->access); printf("file_type: $%.8lX\n", RHptr->file_type); } printf("extra_type: $%.8lX ", RHptr->extra_type); printf("storage_type: %s\n", RHptr->storage_type < STn ? ST[RHptr->storage_type] : unknownStr); printf("created: %s ", PrintDate(&RHptr->create_when, FALSE)); printf("mod: %s\n", PrintDate(&RHptr->mod_when, FALSE)); printf("archived: %s\n", PrintDate(&RHptr->archive_when, FALSE)); printf("GS/OS option_size: %.4x\n", RHptr->option_size); /* future expansion... */ } else { printf("%s internal error: undefined output format\n", prgName); Quit (-1); } /* Print thread info */ if (prtform == FULL) DumpThreads(RNodePtr); RNodePtr = RNodePtr->RNext; /* advance to next record */ #ifdef APW if (STOP()) Quit (1); /* check for OA-period */ #endif } /* end of archive processing */ if (prtform == ARCZOO) { printf( " === ======== ==== ========\n"); printf("Total "); printf("%3ld ", total_files); printf("%8ld ", total_length); /* figure out the percent size, and format it appropriately */ if (!total_complen && !total_length) { printf(" 0%% "); /* file is 0 bytes long */ } else if ((!total_length && total_complen) || (total_length && !total_complen)) { printf("--- "); /* something weird happened */ } else if (total_length < total_complen) { printf(" <0%% "); /* compression failed?!? */ } else { /* compute from sum of thread lengths (use only data?) */ percent = 100 - (int) ((total_complen * 100L) / total_length); if (percent == 0 || percent == 100) (void) sprintf(tmpbuf, "%d%%", percent); else (void) sprintf(tmpbuf, "%.2d%%", percent); printf("%4s ", tmpbuf); } printf("%8ld\n", total_complen); } else if (prtform == FULL) { printf("\n*** end of file position: %ld\n", archive->nextposn); } /* else do nothing */ } filename); datakind = ScanThreads(RNodePtr, &format, &dataCEOF, &dataEOF); printf("%8ld ", dataEOF); printf("%-8.8s ", format < BTFn ? BTF[format] : "Unknown"); /* figure out the percent size, and format it appropriately */ /* Note RNodePtr->comp_len nulib/nulib.doc 644 4170 1464 110107 5260264424 7155 NuFile eXchange (NuFX) Archive Utility NuLib v3.2 Documentation By Andy McFadden Updated September 23, 1992 Overview -------- NuLib is a shell-based NuFX archive utility, based loosely on "ARC" for the IBM PC and "ar" under UNIX. It allows you to perform certain operations on the same archives used by ShrinkIt, including view archive contents, add to archive, extract from archive, and delete from archive. In addition, it will list and unpack files from Binary II archives. This program is primarily targeted at users of non-Apple II computer systems, from IBM PC compatibles to Sun workstations to Amdahl mainframes. It will also be of use to people who use a GS/OS shell on the Apple //gs, such as APW, ORCA, or ECP-16 (although it is considerably slower than ShrinkIt). NuLib may require more than 256K of free RAM to function properly. Apple //gs users can use YankIt instead, which is much smaller and many times faster (though considerably less powerful). Background ---------- For our purposes, an archive is simply a collection of one or more files stored as a single file. This allows groups of related files to be stored under a single filename, reducing directory clutter, and makes it possible to transfer groups of files without the use of a batch transfer utility. To reduce the space required to store the archives and the time it takes to transfer archives, most popular archiving programs automatically compress files as they are stored. Two popular compression methods are Huffman (variable-size codes are used, with smaller codes assigned to bytes that appear frequently) and Lempel-Ziv-Welch (LZW; finds common substrings in the file). Popular archiving programs include "BLU" (Apple II), "ShrinkIt" (Apple II), "GS/ShrinkIt" (Apple IIgs), "ARC"/"PKARC" (MS-DOS), "PKZIP" (MS-DOS), "lharc" (MS-DOS/Amiga), "zoo" (MS-DOS/Amiga), "PackIt" (Macintosh), "StuffIt" (Macintosh), "tar" (UNIX), and "ar" (UNIX). UNIX archivers don't generally compress files; a separate program (called "compress" of all things) is usually used. NuLib works with archives that ShrinkIt produces (NuFX). New Features/Bug Fixes ---------------------- Major changes since last release: + Compression speed improvement + Can add a file as if it were a disk image How to Use NuLib ---------------- Usage: nulib option[suboption] archive [filespec1] [filespec2] [...] "option" is a single character, as specified below. "suboption" is one or more characters, which modify the performance of the option (see each specific option for details). "archive" is the name of a NuFX archive. It doesn't need to exist for the add, create, and move options. "filespecN" is the name of a file, either on disk or in the archive. More than one file may be named on the command line. The option/suboption string may be entered in upper or lower case, and may be preceeded by a hyphen (ex: "nulib -XT0 ..." is equivalent to "nulib -xt0 ..." and "nulib xt0 ..."). Suboptions may be entered in any order. Side note: Under APW, both the archive filename and all file specifications relating to files ON DISK are expanded by NuLib (device names, wildcards, etc). Under UNIX, all filename expansion and wildcards are handled by the shell or the kernal. The filenames of files in the ARCHIVE do not undergo wildcard expansion on any system. Examples: $ nulib av foo.shk file1 subdir/file2 Adds "file1" and "subdir/file2" to "foo.shk", listing each file as it is archived. $ nulib av foo.shk subdir Recursively adds all files in subdir and all files in subdirectories of subdir. $ nulib mv foo.shk =.doc (APW) $ nulib mv foo.shk *.doc (UNIX) $ nulib mv foo.shk *.doc (MS-DOS) Adds all files with the suffix ".doc" to the archive "foo.shk", listing them as it goes, then deletes them from the disk. $ nulib d+ foo.shk work/ Deletes all files in "foo.shk" that start with "work/"; this would likely be used to delete all files archived from the directory "work". No output is produced. The various options are: *** Add/Append files: nulib A[V][U|C|S][R][F[/]][D] archive files Quickly appends the specified files to the end of the archive. This option does not scan the archive to see if the files already exist, and will create a new archive file if one is not found. This option corresponds roughly to N)ew archive or A)dd files on the ShrinkIt menu. If you add files from the current directory or a subdirectory of the current directory, then the entire partial pathname is stored (ex: "nulib av fubar.shk foo/bar/filename" will store "foo/bar/filename" in the archive "fubar.shk.") If you add files from directory that is not a subdirectory of the current directory, only the filename will be stored (ex: "nulib av fubar.shk ../zip/bang/filename" will store "filename" in the archive). Unfortunately, this does not work under UNIX, since filenames are expanded by the kernel instead of by NuLib. Wildcards are allowed, and subdirectories are recursively descended (the depth is limited only by the maximum length of a pathname and the number of files in the subdirectories), unless the R suboption is used. The R suboption will prevent subdirectories from being processed, so that "nulib avr fubar.shk *" under UNIX will add all files in the current directory, but will not descend into any subdirectories. The V suboption displays the filenames as they are added. The U suboption adds a file without compressing it. The C suboption allows you to insert a file in the archive with a specific compression algorithm (specified by the argument "value", which must immediately follow the suboption - no spaces between. Ex: "nulib ac5v fubar.shk file1" would store "file1" in "fubar.shk" using 16-bit UNIX-style LZW compression). "value" corresponds to the appopriate thread_format as specified in the section on compression methods. The S suboption does not compress the file, but will store it as if it were by setting the thread_format field to "value". One use for this is adding compressed files without having to uncompress them first (ex: "nulib as1 foo.shk file1.qq" adds a squeezed file). This suboption could conceivably cause a NuFX extractor to crash; please don't use it unless you are sure of what you are doing. Note also that some compression methods (like ShrinkIt LZW) require both the "compressed length" and "uncompressed length" values to be correct; use of this suboption will (incorrectly) set them both to the current file length. Only one of U, C, or S may be specified. The F suboption allows specification of the filetype attribute. It should be three characters long, and will be matched against a list of standard ProDOS file types. Common ones are "BIN" (binary), "TXT" (text), "SRC" (source code), "OBJ" (object code), "EXE" (executable), and "NON" (typeless, the default). Both upper and lower case letters will match. If the filetype is followed by a "/", then the auxtype field will also be set. NuLib expects the auxtype to be a four-byte hexadecimal number; entering less than four digits could cause the auxtype to be misinterpreted. A good example is storing APW C source files: "nulib cvfSRC/000a nulib.shk file1.c file2.c ...". If the D suboption is given, the entry will be tweaked to make it appear that the file is actually a disk image. This may be useful somewhere. Don't use it if you don't know what you're doing. Adding comments is not currently supported. *** Binary II Operations nulib B[X][V][T][I] binary2-archive [archived-files] This option is included for compatibility with the older Binary II standard, since it is still in use by many communications programs. With no suboptions, this will list the information on the archived files specified by "archived-files". The X suboption extracts the named files from the archive, unsqueezing them if necessary. If the archived-files parameter is omitted, then all files will be listed or extracted. Note that matches are case-independent. When the V suboption is used in conjunction with the X suboption, the names of the archived files are printed as they are extracted. The V suboption has no effect otherwise. The T suboption will perform a text substitution as it works; see the section of conversion of line terminators for more information. If the filename of an extracted file would conflict with an existing file, the file will be overwritten and a message will be printed. If the "I" suboption is used, then NuLib will give an "overwrite (y/n)?" prompt. An affirmative answer overwrites the file; a negative answer will skip the file and continue processing. Attempting to perform NuFX operations on a Binary II archive will fail. If the file appears to be Binary II format, then a message indicating this will be printed. Providing transparent support for Binary II archives is not impossible, but isn't needed often enough to be worth doing. *** Create Archive nulib C[V][U|C|S][R][F[/]][D] archive files This is identical to the A option, but the "creating archive" message is suppressed. This behavior is similar to "ar". *** Delete from Archive nulib D[V][+] archive archived-files Deletes the named files from the archive. NuLib scans the archive, marking all records that match the names in the file specification (case-independent). If all files are marked for deletion, then the archive file itself is deleted. Otherwise, a new archive is created, the unmarked records are transferred, and the old archive is deleted. An error during this process will leave the original archive unmodified. Note that this does not require an exact match if the "+" suboption is used; "nulib d+ fubar.shk foo" will delete "foo", "Foozle", "food/recipies" and "food/stock." The V suboption prints the list of marked records as it works. *** Freshen Archive nulib F[V][U|C|S][R][F[/]][D] archive files Updates files in the archive, but doesn't add any new files. Creates a new archive file, and either transfers the old record or adds the file depending on which is more recent. Only exact filename matches are allowed (case-independent), including partial paths. The archive being updated must already exist. Wildcards are allowed, and subdirectories are automatically expanded unless the R suboption is used. The V suboption displays the filenames as they are added. The U/C/S/F suboptions, explained under A)dd, only apply to files being added to the archive or being updated (files that aren't updated are left unaltered). Files that are updated will retain their previous filetype. New files will get either the default filetype, the filetype specified by the F suboption, or the actual filetype (under APW only) in that order. *** Command Help nulib H[NWS] Displays a help screen. Three screens are available. "nulib h" alone displays help on the options. The N suboption gives help with numbers; it lists the known compression methods and text translation types. The W suboption gives a brief listing of contributors, and how to contact me. The S suboption gives help on the suboptions. *** Integrity Check nulib I[V] archive Verifies that the archive is intact. Does not modify the archive in any way. The V suboption prints a list of CRCs for each entire record (this is different from those listed by the TZ option, which are only for the record headers and (sometimes) threads; this includes not only the headers but *all* data as well). Please note that this doesn't do much more than read the file, unless the record_version is $0002 or greater (which means that the data has a checksum stored; currently these records are only generated by GS/ShrinkIt), AND no compression was used. This merely scans the records and verifies the header CRCs, NOT the data CRCs. The main purpose of the V suboption is to make a list of CRCs that can be sent along with the archive. *** Move Files to Archive nulib M[V][U|C|S][R][F[/]][D] archive files This is identical to the A option, but the files are deleted after they are added to the archive. Note that the actual directory files are NOT deleted, unless they were given distinct record entries. Care should be taken to avoid trying to M)ove an archive into itself. The act of adding may (depending on the OS and the archive) go into an infinite loop creating a huge file, and the coup de grace is when NuLib then deletes the archive you were adding to. *** Print an Archived File nulib P[V][T][+] archive files Print the contents of an archived file without extracting it. Useful for viewing archived text files without having to actually unpack them. Note this only allows viewing of data_threads; resource forks and disk images will not be displayed, and comments are not shown. I take no responsibility for pagination or filtering of funky control characters... The V suboption will print the file's name right before it is extracted. The + suboption allows you to specify the first part of a pathname; see the D or X options for details. The T suboption will perform a text substitution as it works; see the section on conversion of line terminators for more information. *** Table of Contents nulib T[VAZ] archive With no suboptions, this lists only the filenames of the archived files. Not only does this make it easier to view the archive contents (the ShrinkIt format filename field is about 20 characters wide; this is as wide as it has to be), but the output is suitable for transmission via a pipe to other utilities. Using the V suboption will make it use ShrinkIt v2.0 output format (same as using the V option); it is included as a suboption mainly for people used to "ar". Using the A suboption will produce a list similar to the output of ARC or ZOO. Using the Z suboption will dump everything known about the archive, including all information in headers, CRCs, relative file position, sizes of individual threads, etc. *** Update Archive nulib U[V][U|C|S][R][F[/]][D] archive files Updates files in the archive, keeping the archived file or the file listed on the command line, whichever is most recent (or exists). Unlike freshen, this will add new files as necessary. Creates a new archive file, and either transfers the old record or adds the file. Only exact filename matches are allowed (case-independent), including partial pathnames. The archive being updated must already exist. Wildcards are allowed, and subdirectories are automatically expanded unless the R suboption is used. The V suboption displays the filenames as they are added. The U/C/S/F suboptions, explained under A)dd, only apply to files being added to the archive (files that aren't updated are left unaltered). Note that the order of files in the archive will be preserved. Files that are updated will retain their previous filetype. New files will get either the default filetype, the filetype specified by the F suboption, or the actual filetype (under APW only) in that order. *** Verbose Archive Listing nulib V archive Lists the archive contents in a format indentical to that used by the ShrinkIt v2.0 L)ist archive contents option. Same thing as "-tv". *** Extract from Archive nulib X[V][T][U][I][M][+] archive [archived-files] nulib E... The X and E options are synonymous. Extract the archived-files from the archive. If the file already exists, you are asked if you want to overwrite it. If part of a partial pathname does not exist, the subdirectory will be created. Omitting the "archived-files" specification will cause the entire archive to be unpacked. When files are archived, a pathname separator is stored (e.g., "/" for ProDOS and UNIX, "\" for MS-DOS, ":" for Mac HFS). During extraction, the pathnames are broken down into component file names, converted to names legal under the current operating system, and then recombined using the pathname separator for the current OS. This facilitates extraction of files archived under any OS, but can lead to filename conflicts that didn't exist when the files were added (e.g., a UNIX file that contained a backslash is unpacked under MS-DOS). If the filename of an extracted file would conflict with an existing file, the file will be overwritten and a message will be printed. If the "I" suboption is used, then NuLib will give an "overwrite (y/n)?" prompt. An affirmative answer overwrites the file; a negative answer will skip the file and continue processing. Note that extraction does not require an exact match if the "+" suboption is specified; "nulib x+ fubar.shk foo" will extract "FOO", "Foozle", "food/recipies" and "food/stock." This makes it possible to extract entire subdirectories at a time. The V suboption prints the list of marked records. If comments are present, then the V suboption also prints those as it extracts. Using the M suboption will cause the comments to be printed, but the files will not be extracted. **NOTE**: the exact use of the M suboption has not been entirely settled. Since it may be desirable to extract comments into a file, future versions of NuLib may use eXtract to put them in a file and Print to view them. Comments welcome, caveat emptor, have a nice day. The T suboption will perform a text substitution as it extracts; see the section on converson of line terminators for more information. The U suboption will extract the files without uncompressing them. This is rarely useful, but is easy to implement and is present for the curious. Non-NuFX files -------------- NuLib will only work with Binary II and NuFX archives. If you try to view some other kind of file, you will get an error message and an indication of what kind of file NuLib thinks it is. NuLib can recognize files processed with compress, Zip, Zoo, StuffIt, ar, shar, GIF, and many others. Compression Methods ------------------- The following methods are defined in the NuFX documentation: (# = method number, Name = method name [abbreviation], Pack? and Unpack? refer to the ability of NuLib to perform that operation using the given compression method. # Name Pack? Unpack? 00 Uncompressed [unc] Y Y 01 SQueezed [squ] N Y 02 Dynamic LZW I (ShrinkIt) [shk] Y Y 03 Dynamic LZW II (ShrinkIt) [sh2] N Y 04 12-bit UNIX Compress [u12] Y Y 05 16-bit UNIX Compress [u16] Y Y Attempting to use a compression method that does not exist will result in an error message like "[can't squeeze; storing]...". This means that the compression method you requested is unavailable, and it simply stored the file without compression. If you try to extract a file that has been compressed with an algorithm that NuLib is not familiar with, an error message will be printed and the file will not be extracted. Converting Line Terminators --------------------------- Different operating systems use different line terminators in text files. The table below shows them for some popular systems: Operating System Line Terminator Apple ProDOS CR ($0d) UNIX LF ($0a) MS-DOS CRLF ($0d0a) While NuLib will know what kind of terminators the operating system it is running under uses, it cannot reliably determine what kind an archived file uses. Thus, the terminator used on the system where the file was created must be specified. Note that text translation should *not* be performed on non-ASCII files (non-ASCII means anything other than pure text; word processing files and executables are two examples of files that should *never* have text translation performed on them). Doing so will probably ruin the file, causing strange errors. Because of the wide range of files that NuLib must handle, it is impossible to automatically determine which files are binaries and which are text. In order to tell NuLib what format to expect, you have to specify a value parameter (although it will default to zero if you don't). The following examples illustrate their usage: nulib xt0 ... Convert from CR (Apple II -> current system) nulib xt1 ... Convert from LF (UNIX -> current system) nulib xt2 ... Convert from CRLF (MS-DOS -> current system) Shell Variables --------------- NuLib looks for a shell variable called "NULIBOPT" to get default values for certain things. This must be an environment variable ("setenv" using csh, "set" and "export" using sh or APW), and may contain the following: verbose Default to Verbose output (otherwise Silent). type=xxx Default type for storing files (under UNIX or MS-DOS). "xxx" should match the 3-letter ProDOS abbreviation (NON, BIN, TXT, SRC). Normally "NON". aux=xxxx Default auxtype for storing files (under UNIX or MS-DOS). "xxxx" is a four-character hex number (000a, 8002, abcd). Normally "0000". interactive Default to Interactive mode (prompt before overwriting files). compress=n Set default compression to type n. This is useful if you prefer 16-bit UNIX compress (which would be compress=5). Note that the type= and aux= settings do NOT apply when running under APW. Files will be stored with their actual filetypes, regardless of the variable setting. Also, the 'F' suboption will override these settings. An example from csh: setenv NULIBOPT type=BIN,compress=5,interactive ** WARNING: As of this writing, GS/ShrinkIt 1.0.6 and ShrinkIt v3.4 are not able to extract archived files compressed with UNIX compress. It is likely that GS/ShrinkIt will be able to in the future, but the //e version of ShrinkIt will probably never handle them. Error Handling -------------- Many errors simply cause the program to exit, leaving the archive in an uncertain state (which sounds fairly evil). If you were extracting, deleting, viewing, or updating when the error occurred, the worst that can happen is you will be left with a bogus temporary file in the current directory (something like "nulib.tmp5610"). If you were adding to an existing archive, the files that were there will be unharmed, but additional files will not appear, and the archive will be oversized. This is because the master header block (which keeps a count of the number of records in the archive) is written last. If you were creating a new archive, the file will be guano. This is because, as mentioned before, the Master Header Block is not written until the very end. Since NuLib identifies NuFX archives by looking at certain bytes in the MHB, the file will not be identifiable as NuFX. Note that the M)ove option is safer than it looks, because files on disk are not deleted until the archive is safely closed. Revision History ---------------- NuLib v3.22 (September 1992) - improved speed of LZW-I compression - added ability to add files as if they were disks (patches provided by somebody whose name I've lost) - updated the list of 3-letter filetype abbreviations NuLib v3.21 (August 1992) - minor stuff NuLib v3.2 (April 1992) - fixed two bugs in the LZW-II uncompression. - made it compatible with SysV Expanded Fundamental Types (stuff like eight byte file offsets on lseek()s). NuLib v3.11 - v3.14 (November 1991) - fixed a problem with LZW-II uncompression. - added fixes to make it compile on a NeXT and with MS C 6.0. - fixed XENIX directory compatibility problems. NuLib v3.1 (October 1991) - added ability to uncompress LZW-II compression (GS/ShrinkIt). - improved speed of compression routines. - improved System V compatibility. - cleaned up code a bit and fixed minor bugs. Nulib v3.01 - v3.03 (February 1991) - fixed XENIX problems with includes and libs. - fixed bug in directory expansion. - silenced screaming about bad dates. - fixed glitches in nulib.lnk and nulib.mak - fixed non-compression bug in ShrinkIt LZW. NuLib v3.0 (Sep 1990): - added ability to compress files using ShrinkIt LZW-I compression (replaces "fake" compression used previously). - added "compress" parameter to NULIBOPT environment variable. - added ARCZOO output format for people used to MS-DOS archivers. - added M suboption for comment ("message") printing. NuLib v2.3 (May 1990 - not released): - addition of UNIX compress. - threw in some benchmarks. NuLib v2.2 - v2.22 (Apr 1990): - second release as NuLib (after a brief vacation). - fixed incompatibility problems with GS/ShrinkIt and ShrinkIt v3.0. - added some support for comments. - unpacks disk archives to files (good for the UNIX Apple ][+ simulator). - fake compression correctly handles files that don't compress (it used to store them as compressed whether or not they got smaller). - various minor changes/improvements (more help screens, etc). NuLib v2.1.1 (Nov 1989): - first wide distribution as NuLib. - fixed command processing bugs. NuLib v2.1 (Nov 1989): - yet another name change (thanks loads, L&L). - ShrinkIt LZW uncompression added. - MS-DOS code added. - shell variable used for defaults. - CRLF translation. - added R, I, and F suboptions. CShrink v2.08 (Oct 1989): - altered help screens, some commands. - added recognition of other kinds of files (compressed, shar, etc). - switched to table lookups for calculating CRC (much faster). CShrink v2.07 (Sep 1989): - Another name change (legal reasons). - UNIX port completed. - Binary II operations are fully functional. - some compression code added (unSQueeze, fake ShrinkIt pack). - '+' suboption added. - text translation improved. - printing of archived files (P option) now works. NuARC v2.03 (Aug 1989): - first NuARC distribution (APW executable only). - added subdirectory expansion. - added suboption processing. - replaced buffered I/O (fopen(), fread(), etc) on files with lower-level read()/write() routines. - added automatic byte-order determination. - implemented move, extract all, and update/freshen. - added Print archived file option. - added Verbose, Text translation, and Uncompressed storage suboptions. - wrote this documentation. NuARC v2.0 (Aug 1989): - added archive manipulation routines (EXtract, Add and Delete for uncompressed archived files). - added filename-only output format. - added CRC verification. - added byte order and data element size checks. - removed the LAMESEEK option. NuView v1.2 (July 1989): - major overhaul of all source code to make it work under APW C. - new //gs-specific routines added. - minor alterations to output format. NuView v1.1 (June 1989): - major rewrite of the way archives are read (had problems with machines requiring word-alignment of data). - added thread file position storage to internal archive structure. - fixed non-(void) sprintf() bug. NuView v1.0 (May 1989): - initial release. - works only on a Sun 3/50 running BSD UNIX. Limitations ----------- NuLib works just fine with records containing more than one thread (i.e., comments and resource forks). However, while comments can be printed, they can't be added. The big problem this program has is speed. Since it is meant to be portable first and efficient second, it won't run as fast as something like ShrinkIt (written entirely in assembly). What I envision is people using ShrinkIt at home on their Apple //s, but NuLib on UNIX systems or other microcomputers. This will facilitate transfers of large compressed files, which can then be quickly unpacked on the destination system (which will likely have greater computing power, or a C compiler more efficient than APW). System Dependencies ------------------- When compiling this on a Sun 3/50, I noticed a problem: the byte ordering for the //gs (65816) and the Sun (68020) are backward. The present version of NuLib will automatically determine the byte ordering and treat data appropriately (although this may fail if the data size definitions are wrong). There are definitions for one byte, two byte, and four byte variable types; my compiler uses char, short, and long. If these are different for your compiler, be sure to change the typedefs in "nudefs.h". If you don't have 8-bit bytes, though, it may not work (most machines do). Notes on UNIX implementation: If this is being run under UNIX, you should #define UNIX in "nudefs.h". This will enable certain UNIX functions (like system() calls and time routines), and disable others (like file types and "binary mode" for file I/O). If this is being run under BSD UNIX, you should also #define BSD43 in "nudefs.h"; certain small differences were unavoidable (strrchr() vs rindex()). System V users should #define SYSV. See "nudefs.h" for examples. Under APW, breaking down the full pathnames into relative pathnames is easy (and should be under MS-DOS as well...). Under UNIX ".." is an actual directory link, so obtaining a fully expanded pathname with no redundancies is difficult. Thus, storing "dir1/../dir1/foo" would appear as "dir1/foo" under APW but "dir1/../dir1/foo" under UNIX. Care must be taken when extracting such files on non-UNIX systems; they are best avoided entirely. One nasty pitfall is using "~/foo" under the C shell... If the UNIX owner access permissions include write permission, the file will be stored as unlocked. If write permission is denied, it will be stored as locked. As far as dates are concerned, the modification and access times will both be set to the modification time. Notes on //gs implementation: There is very little //gs-specific code, except where absolutely necessary (time routines, file type handling, etc). GS/OS support is absent (extended files are not handled yet, different file systems are not recognized, and pseudo-mixed-case ProDOS filenames are stored as upper case). Some of the faults belong to APW... Unfortunately, the startup code (2/start) provided with APW and the methods of argument passing used by ECP-16 and ProSel-16 aren't compatible. This means that you can run NuLib from ECP or ProSel, but you can't pass it any arguments (which pretty much defeats the purpose...). At any rate, the program is linked with the compression routines in dynamic load segments, so that simple operations should run faster. Additionally, it should be restartable from memory. [actually, the ZapLink version doesn't have dynamic segments...] There is a bug (not my fault) when using the P option to print a file. The file gets printed on one line for some reason... If you redirect the I/O to a file, everything comes out fine. Weird. APW shell-specific code has been avoided where possible. Places where I couldn't easily work around it include wildcard expansion and ERROR(). It was deliberately used in several places to allow the user to STOP() processing with Apple-period. NOTE: NuLib was written to work with APW C, ** NOT ** Orca/C. Attempting to compile it with Orca/C would be a major undertaking (as several people who have tried and failed can attest to). Notes on MS-DOS implementation: The user interface may be slightly different from the UNIX and APW versions, so that MS-DOS users will feel more at home when using the program. The only major deviation is in the handling of subdirectory expansion. A future version may select the ARC/ZOO output format as default. Bugs / Glitches --------------- UNIX lseek uses longs, which are usually four bytes. Signed. If an archive is larger than 2 gigabytes, there may be a problem (cough). This isn't a problem if the system has EFT. Pathnames longer than the #defined maximum (1024 bytes) will not be processed. This is the limit on most machines, and is well in excess of most people's sanity. Pathnames with a null ('\0') in them should generally be avoided. Some partial pathname comparisons may fail because pathname separators vary between operating systems. File naming conventions can result in collisions (ex: "foo+" and "foo~" are unpacked under ProDOS, where both are translated to "foo."). Also, you will probably need to be in the same directory each time you U)pdate an archive, or else the partial pathnames won't match (update requires an EXACT match), and you'll end up with two different copies of the same file. A maximum of 255 files may be added/deleted/whatever at at time. Expanded subdirectories count. This is an arbitrary number and is easy to change if someone can convice me that you'd need to archive more than 255 files at a time. The same file may be added/extracted/whatever more than once if the user enters multiple file selectors on the command line ("nulib av foo.shk file1 file1 file1"). This means that U)pdate could insert the same file twice, etc. NuLib does not at present prevent an archive from being added to itself. This can have unfortunate consequences, especially in conjunction with the M)ove option. The ProDOS three-letter filetype names may or may not be the offical Apple versions. Some users may have problems with things like "$00" because '$' is a csh metacharacter (job control). Error output is informative but ugly (mostly debugging-type messages). Error handling is somewhat fatal in most cases. The code is fairly large, currently around 260K (around 7000 lines of C). It takes a while to compile it, and can be difficult if you only have 1.25MB of RAM (kill your DAs and purge before you link!) In the Works ------------ This is a wishlist of sorts. Don't hold your breath: - Allow CR <-> LF translation when ADDING files. - Improve GS/OS handling. Need to handle resource forks and file system IDs. Would probably help if I had a GS/OS reference... - Add to thread methods (insert ASCII messages, etc). - Add different compression methods (LZH?). Author Info ----------- I'm currently an Associate Software Engineer for Amdahl Corp. in Santa Clara, California. e-mail: fadden@uts.amdahl.com (Andy McFadden) No relation to the author of ShrinkIt and NuFX, Andy Nicholas. This was and continues to be my idea; throw all kudos and blame in my general direction. All code herein is mine, developed from the NuFX Documentation Revisions three through five (the latter from Call -A.P.P.L.E.), except as noted below. Dynamic LZW-I (ShrinkIt) by Kent Dickey. LZW-II compression (LZW-I with table clears) designed by Andy Nicholas. C implementation by Frank Petroski and Kent Dickey (independently and simultaneously). MS-DOS code by Robert B. Hess and Bruce Kahn. The Binary II routines and the unSQueeze code were adapted from the C source by Marcel J.E. Mol (usq.c based on usq2/sq3 by Don Elton). UNIX compress code from COMPRESS v4.3 (see source for detailed author information). If you have suggestions or comments and can't send mail to my Internet address, you can send US mail to: Andy McFadden 1474 Saskatchewan Dr. Sunnyvale, CA 94087 Andy Nicholas, the author of ShrinkIt and affiliated products (ShrinkIt ][+, UnshrinkIt ][+, GS/ShrinkIt), may be contacted at: Internet : shrinkit@Apple.COM CompuServe : 70771,2615 GEnie and America OL : shrinkit Legal Stuff ----------- (I'm not sure about all of these, but I'd like to cover all the bases): ShrinkIt is a trademark of L&L Productions, Inc (...or is it?). ARC is owned by SeaWare. Apple is a tradmark of Apple Computer, Inc. MS-DOS is (probably) a trademark of Microsoft, Inc. PKZIP is (probably) a trademark of PKWare. I have not seen source code for ShrinkIt(tm) or for the compression routines used within it, except as provided to me in C by Kent Dickey. Disclaimers, borrowed from Dave Whitney and Andy Nicholas: This program is FREEWARE; both source and binaries may be distributed freely, but not sold. If you wish to include NuLib in the distribution of a commercial product, you will need to get my permission beforehand. Distribute as widely as possible, but please include this documentation with the binaries and/or sources. Copyright (C) 1989-1991 by Andy McFadden. All rights reserved. I (Andy McFadden) MAKE NO WARRANTY ON THIS MANUAL OR SOFTWARE, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO QUALITY, MANUAL'S ACCURACY, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL I BE HELD RESPONSIBLE FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT OF THE SOFTWARE OR INACCURACY IN THE MANUAL. In other words, I have tested the program to the best of my ability, and I find that as distributed by me, it is safe for general use. It isn't necessarily bug-free, and as a result, loss of data, however unlikely, is entirely possible. Use at your own risk, but also for your own enjoyment. file selectors on the command line ("nulib av foo.shk file1 file1 file1"). This means that U)pdate could insert the same file twice, etc. NuLib does not at present prevent an archive from being added to itself. This can have unfortunate consequences, especially in conjunction with the M)ove option. The ProDOS three-letter filetype names may or may not be the offical Apple versions. Some users may have problems with things like "$00"ptr->storage_type] : unknownStr); printf("created: %s ", PrintDate(&RHptr->create_when, FALSE)); printf("mod: %s\n", PrintDate(&RHptr->mod_when, FALSE)); printf("archived: %s\n", PrintDate(&RHptr->archive_when, FALSE)); printf("GS/OS option_size: %.4x\n", RHptr->option_size); /* future expansion... */ } else { printf("%s internal error: undefined output format\n", prgName); Quit (-1); } /* Print thread info */ if (prtform == FULL) DumpThreads(RNodePtr); RNodePtr = RNodePtr->RNext; /* advance to next record */ #ifdef APW if (STOP()) Quit (1); /* check for OA-period */ #endif } /* end of archive processing */ if (prtform == ARCZOO) { printf( " === ======== ==== ========\n"); printf("Total "); printf("%3ld ", total_files); printf("%8ld ", total_length); /* figure out the percent size, and format it appropriately */ if (!total_complen && !total_length) { printf(" 0%% "); /* file is 0 bytes long */ } else if ((!total_length && total_complen) || (total_length && !total_complen)) { printf("--- "); /* something weird happened */ } else if (total_length < total_complen) { printf(" <0%% "); /* compression failed?!? */ } else { /* compute from sum of thread lengths (use only data?) */ percent = 100 - (int) ((total_complen * 100L) / total_length); if (percent == 0 || percent == 100) (void) sprintf(tmpbuf, "%d%%", percent); else (void) sprintf(tmpbuf, "%.2d%%", percent); printf("%4s ", tmpbuf); } printf("%8ld\n", total_complen); } else if (prtform == FULL) { printf("\n*** end of file position: %ld\n", archive->nextposn); } /* else do nothing */ } filename); datakind = ScanThreads(RNodePtr, &format, &dataCEOF, &dataEOF); printf("%8ld ", dataEOF); printf("%-8.8s ", format < BTFn ? BTF[format] : "Unknown"); /* figure out the percent size, and format it appropriately */ /* Note RNodePtr->comp_len nulib/nulib.doc 644 4170 1464 110107 5260264424 7155 NuFile eXchange (NuFX) Archive Utility NuLib v3.2 Documentation By Andy McFadden Updated September 23, 1992 Overview -------- NuLib is a shell-based NuFX archive utility, based loosely on "ARC" for the IBM PC and "ar" under UNIX. It allows you to perform certain operations on the same archives used by ShrinkIt, including view archive contents, add to archive, extract from archive, and delete from archive. In addition, it will list and unpack files from Binary II archives. This program