fadden@cory.Berkeley.EDU (Andy McFadden) (11/09/89)
Make sure you get the documentation (separate posting), and of course, the other four shar files. If you aren't using UNIX, you can try "unshar.c", but you may end up cutting and pasting. If there is sufficient demand I will post a ShrinkIt + BinSCII version of the APW sources. Part 1/5 of NuLib. ----- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # NOTES # Benchmarks # Makefile # make.apw # linkcom # msdos.mak # apwerr.h # crc.h # nuadd.h # nublu.h # nudefs.h # nuetc.h # nuext.h # nupak.h # nupdel.h # nuread.h # nuview.h # This archive created: Thu Nov 9 01:05:06 1989 # By: Andy McFadden () export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'README'" '(925 characters)' if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \!Funky!Stuff! > 'README' NuLib v2.1 README (05-Nov-89) While I have done extensive testing on NuLib, there are certainly a number of places where bugs could lurk. So, I'm asking that you don't distribute this sources beyond the Usenet community for now. This is only the second wide release; the first was back in August, when I first distributed this as NuARC. Since then, dozens of features have been added, including ShrinkIt uncompression, Binary II support, and full support for the UNIX operating system. Still to come are ShrinkIt compression, a more complete MS-DOS version, resource fork handling, and various other compression methods. Please send bug reports, ideas, or gripes to fadden@cory.berkeley.edu, or to one of the addresses mentioned in the documentation or under the "-hw" option. Things to come: - CRLF translations while adding files - ShrinkIt compression - Ability to manipulate the newer ShrinkIt (v3.0) archives !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'NOTES'" '(3409 characters)' if test -f 'NOTES' then echo shar: "will not over-write existing file 'NOTES'" else cat << \!Funky!Stuff! > 'NOTES' Programming notes (05-Nov-89) ----- 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. Sigh. ----- 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, I haven't exactly followed top-down programming practices. It shows. Sigh. 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) 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. The P option (print) is something of a hack. I just sort of stuck it into extract (it's the same thing, except to stdout instead of a file), but I had to bypass some stuff... It can get messy, but it works. This program is still under development... ----- #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). !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'Benchmarks'" '(1057 characters)' if test -f 'Benchmarks' then echo shar: "will not over-write existing file 'Benchmarks'" else cat << \!Funky!Stuff! > 'Benchmarks' Some benchmarks for NuLib: Uncompressing Moria GS (from 320K to 577K): NuLib, APW C version, running on a //gs 12 minutes Shrinkit v2.1, running on a //gs 1 minute 42 seconds NuLib on a moderately loaded VAX 11/780 1 minute 15 seconds NuLib on a partially loaded Sun 3/50 37 seconds NuLib on an Apollo workstation (all by my lonesome) 12 seconds On the Sun 3/50, approximately 12 seconds was devoted to disk access, with the remaining time for computation. On the Apollo, I was able to extract (uncompressed) the whole 577K file in less than 1 second. I'm not sure how things panned out on the VAX, but about 40 people were on at the time. Interestingly enough, 16-bit UNIX compress works faster than NuLib does. The reason for this is that the compression code in NuLib actually makes two passes, one to undo the LZW compression, and the other to undo non-repeat compression. Was it worth it do to both kinds? For Moria it was; even though "uncompress" ran faster, ShrinkIt's 12-bit LZW packed the file smaller than compress's 16-bit LZW. !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'Makefile'" '(1261 characters)' if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \!Funky!Stuff! > 'Makefile' # # UNIX Makefile for NuLib # # To make a smaller executable, you can exclude the Binary II routines # by setting CFLAGS= -DNO_BLU HDRS=nudefs.h nuread.h nuview.h nuadd.h nuext.h nupdel.h nupak.h nuetc.h\ nublu.h SRCS=numain.c nuread.c nuview.c nuadd.c nuext.c nupdel.c nupak.c nuetc.c\ nublu.c nushk.c nusq.c OBJS=numain.o nuread.o nuview.o nuadd.o nuext.o nupdel.o nupak.o nuetc.o\ nublu.o nushk.o nusq.o #CFLAGS=-g CFLAGS=-O LIBS= CC=cc nulib: ${OBJS} ${CC} ${CFLAGS} ${OBJS} -o nulib ${LIBS} 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 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 shar: xshar -v -c -osh.files/nulib -l40 README NOTES Benchmarks Makefile \ make.apw msdos.mak linkcom *.h *.c !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'make.apw'" '(630 characters)' if test -f 'make.apw' then echo shar: "will not over-write existing file 'make.apw'" else cat << \!Funky!Stuff! > 'make.apw' * * NuLib AGE script (A pseudo-makefile for APW and ORCA) * for file in numain nuread nuview nuadd nuext nupdel nupak nuetc nublu nushk nusq 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 alink linkcom prefix .. end !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'linkcom'" '(2119 characters)' if test -f 'linkcom' then echo shar: "will not over-write existing file 'linkcom'" else cat << \!Funky!Stuff! > 'linkcom' ; APW LinkEd command 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 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 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 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... !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'msdos.mak'" '(1923 characters)' if test -f 'msdos.mak' then echo shar: "will not over-write existing file 'msdos.mak'" else cat << \!Funky!Stuff! > 'msdos.mak' /**** SHK-DOS.MAK ****/ LLIBS_G = LLIBS_D = /NOD:LLIBCE LLIBCER LLIBS_R = /NOD:LLIBCE LLIBCER PROJ = SHRINK LFLAGS_R = /NOF LFLAGS_D = /CO /INC /NOF LFLAGS_G = /NOI /NOE ILINK = ilink LINK = link CFLAGS_R = /Ox CFLAGS_D = /qc /Zi /Zr /Gi /Od CFLAGS_G = /AL /W2 CC = cl DEBUG = 0 DEFS = LIBS = OBJS = $(OBJS_C) $(OBJS_EXT) OBJS_C = $(CS_EXT:.c=.obj) OBJS_EXT = ..\..\pmsdk\lib\setargv.obj CS_EXT = nuadd.c nublu.c nuetc.c nuext.c numain.c nupak.c nupdel.c nuread.c \ nushk.c nuview.c nusq.c all: SHK-DOS.exe nuadd.obj: nuadd.c nudefs.h nuread.h nuadd.h nupak.h nuetc.h nublu.obj: nublu.c nudefs.h nuview.h nuadd.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 nuetc.h nupak.obj: nupak.c nudefs.h nupak.h nuetc.h nupdel.obj: nupdel.c nudefs.h nuread.h nuadd.h nupak.h nupdel.h nuetc.h nuread.obj: nuread.c nudefs.h nuread.h nupak.h nuetc.h nushk.obj: nushk.c nudefs.h nuread.h nupak.h nuview.obj: nuview.c nudefs.h nuview.h nuread.h nuetc.h nusq.obj: nusq.c nudefs.h nuread.h nupak.h SHK-DOS.exe: $(OBJS) $(LIBS) $(DEFS) !IF $(DEBUG) echo > NUL @<<$(PROJ).crf $(OBJS: = +^ ),$@,,$(LLIBS_G) $(LLIBS_D) $(LIBS),$(DEFS) $(LFLAGS_G) $(LFLAGS_D); << $(ILINK) -a -e "$(LINK) @$(PROJ).crf" $@ !ELSE echo > NUL @<<$(PROJ).crf $(OBJS: = +^ ),$@,,$(LLIBS_G) $(LLIBS_R) $(LIBS),$(DEFS) $(LFLAGS_G) $(LFLAGS_R); << $(ILINK) -a -e "$(LINK) @$(PROJ).crf" $@ !ENDIF .c.obj: !IF $(DEBUG) $(CC) /c $(CFLAGS_G) $(CFLAGS_D) /Fo$*.obj $*.c !ELSE $(CC) /c $(CFLAGS_G) $(CFLAGS_R) /Fo$*.obj $*.c !ENDIF run: SHK-DOS.exe $(PROJ) $(RUNFLAGS) debug: SHK-DOS.exe CVP $(CVPFLAGS) $(PROJ) $(RUNFLAGS) !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'apwerr.h'" '(6759 characters)' if test -f 'apwerr.h' then echo shar: "will not over-write existing file 'apwerr.h'" else cat << \!Funky!Stuff! > 'apwerr.h' /* * apwerr.h - text versions of APW and ProDOS 16 error codes * ERROR() didn't cut it, and I'm trying to separate things from the shell. * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ /* 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" }; !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'crc.h'" '(3321 characters)' if test -f 'crc.h' then echo shar: "will not over-write existing file 'crc.h'" else cat << \!Funky!Stuff! > 'crc.h' /* * crc.h - tables for fast computation of 16-bit CRCS * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ /* * 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 }; !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nuadd.h'" '(950 characters)' if test -f 'nuadd.h' then echo shar: "will not over-write existing file 'nuadd.h'" else cat << \!Funky!Stuff! > 'nuadd.h' /* * nuadd.h - declarations for nuadd.c * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ /* 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 */ twobyt fileSysInfo; int marked; /* application specific */ } file_info; #define MAXARGS 255 /* max #of files specified on command line; signed int */ extern void NuAdd(), AddFile(); extern onebyt *MakeMHblock(); extern int EvalArgs(); !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nublu.h'" '(182 characters)' if test -f 'nublu.h' then echo shar: "will not over-write existing file 'nublu.h'" else cat << \!Funky!Stuff! > 'nublu.h' /* * nublu.h - declarations for nublu.c * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ extern void NuBNY(); !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nudefs.h'" '(3283 characters)' if test -f 'nudefs.h' then echo shar: "will not over-write existing file 'nudefs.h'" else cat << \!Funky!Stuff! > 'nudefs.h' /* * nudefs.h - system-dependent typdefs and #defines for all CShrink files, * plus extern declarations for global variables (allocated in * numain.c). * * IMPORTANT: This file must be first on the list of #includes, since some * include files will be processed based on these #defines * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ /* 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) */ /* [ "MSDOS" is defined by the compiler [MS C5.1 anyway] ] */ /* Setup for other microcomputers (Macintosh, Amiga) */ /* ??? */ /* Setup for BSD UNIX systems */ #define UNIX #define BSD43 /* #define VAX */ /* Setup for other UNIX systems */ /* #define UNIX */ /* #define SYSV (or whatever) */ /* #define VAX */ /* use table lookups to get CRCs */ #define CRC_TAB /* * Some global defs */ /* errno wasn't defined in <errno.h> 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 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 */ # endif # ifdef MSDOS # define S_IREAD 0000400 /* read permission, owner */ # define S_IWRITE 0000200 /* write permission, owner */ # define WPERMS S_IREAD | S_IWRITE # endif # ifndef WPERMS /* other system */ # define WPERMS 0666 /* +PORT+ */ # 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 transfrom; /* how to do CR<->LF translation? (-1 = none) */ extern int transto; extern int packMethod; /* how to pack a file (thread_format) */ 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] */ !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nuetc.h'" '(1194 characters)' if test -f 'nuetc.h' then echo shar: "will not over-write existing file 'nuetc.h'" else cat << \!Funky!Stuff! > 'nuetc.h' /* * nuetc.h - declarations for nuetc.c * * (this will be included by almost all source files; it should come last) * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ /* define these if they haven't been already */ #ifndef TRUE typedef int BOOLEAN; # define TRUE 1 # define FALSE 0 #endif #ifdef UNIX # ifdef BSD43 extern char *index(); /* BSD version */ extern char *rindex(); # define INDEX index # define RINDEX rindex # else extern char *strchr(); /* AT&T version */ extern char *strrchr(); # define INDEX strchr # define RINDEX strrchr # endif #else extern char *strchr(); /* APW, MSC */ extern char *strrchr(); # define INDEX strchr # define RINDEX strrchr #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(); !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nuext.h'" '(251 characters)' if test -f 'nuext.h' then echo shar: "will not over-write existing file 'nuext.h'" else cat << \!Funky!Stuff! > 'nuext.h' /* * nuext.h - declarations for nuext.c * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #define MAXDEPTH 64 /* max subdir depth we will unpack */ extern void NuExtract(); !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nupak.h'" '(880 characters)' if test -f 'nupak.h' then echo shar: "will not over-write existing file 'nupak.h'" else cat << \!Funky!Stuff! > 'nupak.h' /* * nupak.h - declarations for nupak.c * * 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. * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ /* 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 */ !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nupdel.h'" '(205 characters)' if test -f 'nupdel.h' then echo shar: "will not over-write existing file 'nupdel.h'" else cat << \!Funky!Stuff! > 'nupdel.h' /* * nupdel.h - declarations for NuDelete * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ extern void NuDelete(), NuUpdate(); !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nuread.h'" '(3897 characters)' if test -f 'nuread.h' then echo shar: "will not over-write existing file 'nuread.h'" else cat << \!Funky!Stuff! > 'nuread.h' /* * nuread.h - linked list structures used for holding NuFX header data, * and structure definitions for archive innards * * (this will be included by all source files which access NuFX archives) * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ /* The NuFX record format version we output, and the maximum we can extract */ #define OURVERS 0 #define MAXVERS 0 /* "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; onebyt reserved[20]; } 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; fourbyt total_threads; twobyt file_sys_id; twobyt file_sys_info; /* (hi-bit) sparse | fs_sep (lo-bit) */ fourbyt access; fourbyt file_type; fourbyt extra_type; twobyt storage_type; Time create_when; Time mod_when; Time archive_when; /* future expansion here... */ } RHblock; #define RHsize 56 /* sizeof(RHblock) should work, but might not */ #define ATTSIZE 64 /* default attrib_count when creating ( > RHsize!) */ /* * 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 56 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 reserved; 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 */ short namelen; /* length of filename (safer than strlen()) */ 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 */ !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nuview.h'" '(588 characters)' if test -f 'nuview.h' then echo shar: "will not over-write existing file 'nuview.h'" else cat << \!Funky!Stuff! > 'nuview.h' /* * nuview.h - declarations for nuview.c * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ typedef enum { NAMEONLY, PROSHK, 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 3 extern char *TF[]; #define QTFn 3 extern char *QTF[]; #define FIDn 12 extern char *FID[]; #define STn 14 extern char *ST[]; extern char *FT[]; extern void NuView(); extern char *PrintDate(); !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive
fadden@cory.Berkeley.EDU (Andy McFadden) (11/09/89)
NuLib part 2/5 ----- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # nuadd.c # nublu.c # nuetc.c # This archive created: Thu Nov 9 01:07:18 1989 # By: Andy McFadden () export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'nuadd.c'" '(19636 characters)' if test -f 'nuadd.c' then echo shar: "will not over-write existing file 'nuadd.c'" else cat << \!Funky!Stuff! > 'nuadd.c' /* * nuadd.c - operations which add to a NuFX archive (add) * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include <stdio.h> #include <fcntl.h> #include <errno.h> #include <ctype.h> #ifdef BSD43 # include <strings.h> #else # include <string.h> #endif #ifdef UNIX # include <sys/types.h> # include <sys/stat.h> # include <sys/dir.h> #endif #ifdef APW # include <types.h> # include <prodos.h> # include <shell.h> # include <strings.h> #endif #ifdef MSDOS # include <stdlib.h> # include <errno.h> # include <time.h> # include <io.h> # include <sys\types.h> # include <sys\stat.h> #endif #include "nuread.h" #include "nuadd.h" #include "nupak.h" #include "nuetc.h" #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; DIR *dirp; struct direct *dp; 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? */ 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 */ newnames[newcount] = (char *) Malloc(nmlen + dp->d_namlen +1); 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 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 = defAuxType; FIArray[idx]->fileSysID = 0x0001; /* ProDOS */ FIArray[idx]->fileSysInfo = 0x002f; /* not sparse; '/' */ 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 = 0x002f; /* not sparse, '/' */ 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 = defAuxType; FIArray[idx]->storageType = 0x0000; FIArray[idx]->fileSysID = 0x0001; /* ProDOS */ FIArray[idx]->fileSysInfo = 0x001c; /* not sparse, '\' */ 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 = 0x001c; /* not sparse, '\' */ 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. */ void 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 use older attrib_count... */ HiSwap(recBuf, 6, 7); twoptr = (twobyt *) (recBuf+8); *twoptr = OURVERS; /* store new record with our rec vers */ HiSwap(recBuf, 8, 9); /* BCopy(&total_threads, recBuf+10, 4, TRUE); */ BCopy(&infoptr->fileSysID, recBuf+14, 2, TRUE); BCopy(&infoptr->fileSysInfo, recBuf+16, 2, TRUE); BCopy(&infoptr->fAccess, recBuf+18, 4, TRUE); BCopy(&infoptr->fileType, recBuf+22, 4, TRUE); BCopy(&infoptr->auxType, recBuf+26, 4, TRUE); BCopy(&infoptr->create_dt, recBuf+32, sizeof(Time), FALSE); BCopy(&infoptr->mod_dt, recBuf+40, sizeof(Time), FALSE); BCopy(GetTime(), recBuf+48, sizeof(Time), FALSE); twoptr = (twobyt *) (recBuf + (ATTSIZE - 2)); *twoptr = strlen(infoptr->store_name); HiSwap(recBuf, ATTSIZE-2, ATTSIZE-1); /* correct strlen ordering */ thread[0].thread_class = 0x0002; /* data */ HiSwap(&thread[0].thread_class, 0, 1); thread[0].thread_kind = 0x0000; /* data fork */ HiSwap(&thread[0].thread_kind, 0, 1); thread[0].thread_format = 0x0000; /* filled in later */ thread[0].reserved = 0x0000; HiSwap(&thread[0].reserved, 0, 1); /* so I don't forget later on... */ thread[0].thread_eof = infoptr->eof; HiSwap(&thread[0].thread_eof, 0, 3); HiSwap(&thread[0].thread_eof, 1, 2); thread[0].comp_thread_eof = -1L; /* filled in later */ total_threads++; BCopy(&total_threads, 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, 0L, S_REL); /* save record posn */ if (lseek(arcfd, (long) (ATTSIZE + strlen(infoptr->store_name)), S_REL)<0) Fatal("Bad seek (R.rel)", procName); /* loop... */ thposn = lseek(arcfd, 0L, S_REL); /* save thread posn */ if (lseek(arcfd, (long) 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. */ thread[0].thread_format = PackFile(srcfd,arcfd, infoptr->eof, dopack ? packMethod:0, pakbuf); if (!dopack) thread[0].thread_format = packMethod; /* for S subopt */ HiSwap(&thread[0].thread_format, 0, 1); thread[0].comp_thread_eof = (fourbyt) packedSize; HiSwap(&thread[0].comp_thread_eof, 0, 3); /* correct ordering */ HiSwap(&thread[0].comp_thread_eof, 1, 2); tmpposn = lseek(arcfd, 0L, S_REL); if (lseek(arcfd, 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, 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, recBuf+6, ATTSIZE-6); CRC = CalcCRC(CRC, infoptr->store_name, strlen(infoptr->store_name)); CRC = CalcCRC(CRC, &thread[0], THsize); twoptr = (twobyt *) (recBuf+4); *twoptr = CRC; HiSwap(recBuf, 4, 5); tmpposn = lseek(arcfd, 0L, S_REL); /* record posn (next posn) */ if (lseek(arcfd, 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, tmpposn, S_ABS) < 0) /* seek back to where we were */ Fatal("Bad seek (TmpB)", procName); if (verbose) printf("done.\n"); free(recBuf); /* return ( (long) (THsize * total_threads) + (long) (ATTSIZE) + */ /* (long) strlen(infoptr->store_name) + thread[0].comp_thread_eof ); */ return; } /* * 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; onebyt *bufPtr; twobyt *twoptr; 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 */ strncpy((char *) bufPtr+0, (char *) MasterID, 6); BCopy(GetTime(), bufPtr+12, 8, FALSE); BCopy(bufPtr+12, bufPtr+20, 8, FALSE); twoptr = (twobyt *) (bufPtr+6); *twoptr = CalcCRC(0, 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. */ onebyt *MakeMHblock(archive, total_records) ListHdr *archive; fourbyt total_records; { static onebyt buf[MHsize]; /* must be static */ twobyt *twoptr; int idx; static char *procName = "MakeMHblock"; for (idx = 0; idx < MHsize ; idx++) buf[idx] = '\0'; strncpy((char *) buf, (char *) MasterID, 6); BCopy(&total_records, &buf[8], 4, TRUE); BCopy(&archive->MHptr->arc_create_when, &buf[12], sizeof(Time), FALSE); BCopy(GetTime(), &buf[20], sizeof(Time), FALSE); twoptr = (twobyt *) &buf[6]; *twoptr = CalcCRC(0, &buf[8], MHsize-8); HiSwap(buf, 6, 7); /* correct byte ordering */ 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 */ 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, 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 AddFile(arcfd, FIArray[idx]); archive->MHptr->total_records++; } mptr = MakeMHblock(archive, archive->MHptr->total_records); if (lseek(arcfd, 0L, 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 <subopt> */ if (transfrom >= 0) { transto = transfrom; transfrom = -1; } Add(filename, namecount, names); /* do main processing */ } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nublu.c'" '(11007 characters)' if test -f 'nublu.c' then echo shar: "will not over-write existing file 'nublu.c'" else cat << \!Funky!Stuff! > 'nublu.c' /* * nublu.c - operations on Binary II archives * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #ifndef NO_BLU /***********************************/ #include "nudefs.h" #include <stdio.h> #include <fcntl.h> #ifdef UNIX # include <errno.h> # include <sys/types.h> # include <sys/stat.h> #endif #ifdef APW # include <prodos.h> #endif #ifdef MSDOS # include <stdlib.h> # include <io.h> # include <string.h> # include <sys\types.h> # include <sys\stat.h> #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; onebyt buf[BUFSIZE]; long full_len; int offset; static char *procName = "extract_file"; /*n = */ read(fd, buf, 70); /* read first few bytes */ lseek(fd, -70L, 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? */ if (verbose) { printf("(as %s)...", fname); fflush(stdout); } } 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, 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, 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, (long) 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++) == NULL) 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, (long) 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, (long) 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*/ /***********************************/ !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nuetc.c'" '(15995 characters)' if test -f 'nuetc.c' then echo shar: "will not over-write existing file 'nuetc.c'" else cat << \!Funky!Stuff! > 'nuetc.c' /* * nuetc.c - extra stuff; most is #ifdefed to death (mostly time routines * and file stuff that are highly system dependent) * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include <stdio.h> #include <fcntl.h> #include <errno.h> /* errno declarations */ #include <ctype.h> /* for tolower(), isupper() */ #ifdef UNIX # include <time.h> /* need localtime() */ # include <sys/types.h> /* defn of time_t */ # include <sys/stat.h> #endif #ifdef APW # include <stdlib.h> /* exit(), etc. */ # include <types.h> /* has _toolErr in it */ # include <prodos.h> # include "apwerr.h" /* APW/ProDOS error codes */ #endif #ifdef MSDOS # include <process.h> # include <stdlib.h> # include <io.h> # include <sys\types.h> # include <sys\stat.h> # include <time.h> #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 printf("(Largest Available Block: %u)\n", _memmax()); #endif Quit (-1); } } /******************** 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. */ 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() { if (_toolErr) { if (_toolErr < MPErr) { /* was a ProDOS error? */ fprintf(stderr, "Error: $%.2x %s\n", (char) _toolErr, ProDOSErr[_toolErr]); } else { fprintf(stderr, "Tool err: "); fflush(stderr); ERROR( _toolErr ); } 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, 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, 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, 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*/ } 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, "cshk.tmpXXXXXX"); return (mktemp(buffer)); #else # ifdef APW int idx = 0; do { sprintf(buffer, "cshk.tmp%d", idx++); } while (Exists(buffer)); return (buffer); # endif /* APW */ # ifdef MSDOS extern char *mktemp(); strcpy(buffer, "cshkXXXX.tmp"); return (mktemp(buffer)); # endif /* MSDOS */ # ifndef APW # ifndef MSDOS strcpy(buffer, "cshk.tmp"); /* +PORT+ */ return (buffer); # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ } /* * 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); } 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*/ } } #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 return (timecvt(tptr)); #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 */ } !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive
fadden@cory.Berkeley.EDU (Andy McFadden) (11/09/89)
NuLib part 3/5 ----- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # nuext.c # numain.c # nupak.c # This archive created: Thu Nov 9 01:07:30 1989 # By: Andy McFadden () export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'nuext.c'" '(16679 characters)' if test -f 'nuext.c' then echo shar: "will not over-write existing file 'nuext.c'" else cat << \!Funky!Stuff! > 'nuext.c' /* * nuext.c - operations which extract from a NuFX archive * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include <stdio.h> #ifdef BSD43 # include <strings.h> #else /* SYSV, APW, MSC */ # include <string.h> #endif #include <fcntl.h> #ifdef UNIX # include <errno.h> # include <time.h> # include <sys/types.h> # include <sys/stat.h> #endif #ifdef APW # include <types.h> # include <prodos.h> # include <shell.h> # include <strings.h> #endif #ifdef MSDOS # include <io.h> # include <time.h> # include <stdlib.h> # include <errno.h> # include <direct.h> # include <utime.h> # include <sys\types.h> # include <sys\stat.h> #endif #include "nuread.h" #include "nuext.h" #include "nupak.h" #include "nuetc.h" static BOOLEAN extall; /* extract all files? */ static BOOLEAN print; /* extract to screen rather than file? */ /* * Get the answer to a yes/no question. * * Returns TRUE for yes, FALSE for no. May return additional things in the * future... (y/n/q)? */ int AskYesNo() { char buf[16]; /* if user answers with >16 chars, bad things happen */ char c; printf(" (y/n)? "); fflush(stdout); c = *gets(buf); if ((c == 'y') || (c == 'Y')) return (TRUE); else return (FALSE); } /* * Convert a filename to one legal in the present file system. * * Does not allocate new space; alters string in place (so original string * will be "corrupted"). Assumes that it has been passed a filename without * the filename separators. */ void ConvFileName(str) char *str; { int idx = 0; #ifdef UNIX while (*str != '\0') { if ((*str > 127) || (*str < 0)) *str &= 0x7f; /* clear hi bit */ if (*str == '/') *str = '.'; if (++idx > 255) { *str = '\0'; break; } str++; } #else # ifdef APW static char *legal = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789."; /* assumes ProDOS limits, not GS/OS */ if ( ((*str < 'A') && (*str > 'Z')) || ((*str < 'a') && (*str > 'z')) ) *str = 'X'; /* must start with alpha char */ while (*str != '\0') { if (!INDEX(legal, *str)) *str = '.'; if (++idx > 15) { *str = '\0'; break; } str++; } # endif /* APW */ # ifdef MSDOS while (*str != '\0') { if ((*str > 127) || (*str < 0)) *str &= 0x7f; /* clear hi bit */ if (*str == '/') *str = '_'; if (*str == '\\') *str = '_'; if (*str == '!') *str = '_'; if (*str == ':') *str = '_'; if (++idx > 255) { *str = '\0'; break; } str++; } # endif /* MSDOS */ # ifndef APW # ifndef MSDOS printf("Need [other] filename converter\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /*UNIX*/ } /* * Set a file's attirbutes according to info in a record structure. */ void SetFInfo(filename, RHptr) char *filename; RHblock *RHptr; { static char *procName = "SetFInfo"; #ifdef UNIX long ltime; time_t timep[2]; ltime = ReduceTime(&RHptr->mod_when); /* set both to mod time */ timep[0] = ltime; /* accessed */ timep[1] = ltime; /* modified */ utime(filename, timep); if ((RHptr->access == 0xe3L) || (RHptr->access == 0xc3L)) /* unlocked */ chmod(filename, S_IREAD | S_IWRITE | 044); if ((RHptr->access == 0x21L) || (RHptr->access == 0x01L)) /* locked */ chmod(filename, S_IREAD | 044); #else /* UNIX */ # ifdef APW /* * Call ProDOS SET_FILE_INFO to set attributes for a file. * Uses the information in the record header block. */ FileRec finfo; OpenRec oinfo; twobyt date, time; long ltime; finfo.pathname = c2pstr(filename); /* temp storage...? */ finfo.fAccess = (twobyt) RHptr->access; finfo.fileType = (twobyt) RHptr->file_type; finfo.auxType = RHptr->extra_type; finfo.storageType = 0; /* RHptr->storage_type otherwise */ ltime = ReduceTime(&RHptr->create_when); date = (twobyt) ltime; /* date is lower 16 */ time = (twobyt) (ltime >> 16); /* time is upper */ finfo.createDate = date; finfo.createTime = time; ltime = ReduceTime(&RHptr->mod_when); date = (twobyt) ltime; /* date is lower 16 */ time = (twobyt) (ltime >> 16); /* time is upper */ finfo.modDate = date; finfo.modTime = time; SET_FILE_INFO( &finfo ); ToolErrChk(); # endif /* APW */ # ifdef MSDOS long ltime; time_t timep[2]; ltime = ReduceTime(&RHptr->mod_when); timep[0] = ltime; /* accessed */ timep[1] = ltime; /* modified */ utime(filename, timep); if ((RHptr->access == 0xe3L) || (RHptr->access == 0xc3L)) /* unlocked */ chmod(filename, S_IREAD | S_IWRITE | 044); if ((RHptr->access == 0x21L) || (RHptr->access == 0x01L)) /* locked */ chmod(filename, S_IREAD | 044); # endif /* MSDOS */ # ifndef APW # ifndef MSDOS printf("need [other] SetFInfo stuff\n"); /* +PORT+ */ # endif /* none2 */ # endif /* none1 */ #endif /* APW */ } /* * Create a subdirectory * * This routine will exit on most errors, since generally more than one file * will be unpacked to a given subdirectory, and we don't want it charging * bravely onward if it's going to run into the same problem every time. */ void CreateSubdir(pathname) char *pathname; { char *buffer = (char *) Malloc(MAXFILENAME+6); static char *procName = "CreateSubdir"; #ifdef UNIX struct stat st; /* if no directory exists, then make one */ if (stat(pathname, &st) < 0) if (errno == ENOENT) { sprintf(buffer, "mkdir %s", pathname); if (system(buffer) != 0) /* call UNIX mkdir to create subdir */ Fatal("Unable to create subdir", procName); } else { Fatal("Unable to create dir", procName); } #else # ifdef APW static FileRec create_p = { "", 0x00e3, 0x000f, 0L, 0x000d, 0, 0 }; /*dir*/ FileRec info_p; /* check if file exists, is dir */ int err; /* holds _toolErr */ strcpy(buffer, pathname); c2pstr(buffer); info_p.pathname = buffer; GET_FILE_INFO( &info_p ); switch (_toolErr) { case 0x0000: /* no error */ if (info_p.storageType != 0x000d) /* not a DIR? */ Fatal("File in path exists, is not a directory.", procName); return; /* file exists, is directory, no need to create */ case fileNotFound: create_p.pathname = buffer; CREATE( &create_p ); if (!_toolErr) return; /* created okay? */ else ToolErrChk(); default: /* unknown error */ ToolErrChk(); Fatal("whoops!", procName); /* shouldn't get here */ } # endif /* APW */ # ifdef MSDOS struct stat st; /* if no directory exists, then make one */ if (stat(pathname, &st) < 0) if (errno == ENOENT) { if (mkdir(pathname) != 0) Fatal("Unable to create subdir", procName); } else { Fatal("Unable to create dir", procName); } # endif /* MSDOS */ # ifndef APW # ifndef MSDOS /* don't forget to check if it exists first... */ /* +PORT+ */ printf("don't know how to create [other] subdirectories\n"); /* mkdir() */ # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ free(buffer); } /* * Given a pathname, create subdirectories as needed. All file names are run * through a system-dependent filename filter, which means that the pathname * has to be broken down, the subdirectory created, and then the pathname * reconstructed with the "legal" pathname. The converted filename is held * in a static buffer; subsequent calls will overwrite the previous string. * * This is useful when unpacking "dir1/dir2/fubar" and dir1 and dir2 don't * necessarily exist. * * It is assumed that all filenames are relative to the current directory. * According to the NuFX docs (revision 3 2/3/89), initial separators (like * "/", "\", or ":") should NOT be included. If they are, this routine may * break. */ static char *CreatePath(pathname, fssep) char *pathname; /* full pathname; should not include ProDOS volume name */ onebyt fssep; /* file system pathname separator, usually "/" or "\" */ { int idx; char *ptr; static char workbuf[MAXFILENAME]; /* work buffer; must be static */ static char *procName = "CreatePath"; idx = 0; while (TRUE) { /* move through string */ ptr = INDEX(pathname, fssep); /* find break */ if (ptr) /* down to actual filename? */ *ptr = '\0'; /* no, isolate this part of the string */ strcpy(&workbuf[idx], pathname); /* copy component to buf */ ConvFileName(&workbuf[idx]); /* convert to legal str; may be shorter */ idx += strlen(&workbuf[idx]); /* advance index to end of string */ if (!ptr) { /* down to actual filename? */ workbuf[idx] = '\0'; /* yes, clean up */ break; /* out of while */ } workbuf[idx] = '\0'; CreateSubdir(workbuf); /* system-dependent dir create */ workbuf[idx++] = fssep; /* tack an fssep on the end, and advance */ *ptr = fssep; /* be nice */ pathname = ptr+1; /* go again with next component */ } return (workbuf); } /* * Extract a thread, and place in a file. * * Returns TRUE if the extract was successful, FALSE otherwise. The most * common reason for a FALSE return value is a "no" answer when asked about * overwriting an existing file. */ static BOOLEAN ExtractThread(arcfd, fileposn, destpn, THptr) int arcfd; /* source file descriptor (must be open) */ long fileposn; /* position of data in source file */ char *destpn; /* destination filename */ THblock *THptr; /* pointer to thread info */ { int dstfd; /* destination file descriptor */ static char *procName = "ExtractThread"; if (!print) { if (Exists(destpn)) { if (interact) { if (verbose) printf("file exists, overwite"); else printf("%s exists, overwite", destpn); if (!AskYesNo()) { /* return w/o overwriting */ return (FALSE); } } if (verbose) { printf("overwriting..."); fflush(stdout); } if (unlink(destpn) < 0) Fatal("Unable to remove existing file", procName); } if ((dstfd = open(destpn, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, WPERMS)) < 0) Fatal("Unable to open target path", procName); if (lseek(arcfd, fileposn, S_ABS) < 0) Fatal("Seek failed", procName); if (!UnpackFile(arcfd, dstfd, THptr->comp_thread_eof, THptr->thread_eof, dopack ? THptr->thread_format : 0, pakbuf)) { if (close(dstfd) < 0) Fatal("Dest close failed", procName); unlink(destpn); /* some sys can't delete while file open */ } else { if (close(dstfd) < 0) Fatal("Dest close failed", procName); } } else { /* print */ if ((dstfd = fileno(stdout)) < 0) Fatal("Unable to get file for stdout", procName); if (lseek(arcfd, fileposn, S_ABS) < 0) Fatal("Seek failed", procName); if (!UnpackFile(arcfd, dstfd, THptr->comp_thread_eof, THptr->thread_eof, dopack ? THptr->thread_format : 0, pakbuf)) { printf("Unpack failed.\n"); return (FALSE); } fflush(stdout); } return (TRUE); } /* * Handle message_threads */ static void message_thread(arcfd, RNodePtr, TNodePtr) int arcfd; RNode *RNodePtr; TNode *TNodePtr; { switch (TNodePtr->THptr->thread_kind) { case 0x000: /* ASCII text */ printf("Found ASCII text thread\n"); break; default: printf("Found unknown message_thread %.4x in '%s'\n", TNodePtr->THptr->thread_kind, RNodePtr->filename); break; } } /* * Handle control_threads */ static void control_thread(arcfd, RNodePtr, TNodePtr) int arcfd; RNode *RNodePtr; TNode *TNodePtr; { switch (TNodePtr->THptr->thread_kind) { case 0x000: /* create dir */ printf("Found create directory control thread\n"); break; default: printf("Found unknown control_thread %.4x in '%s'\n", TNodePtr->THptr->thread_kind, RNodePtr->filename); break; } } /* * Handle data_threads * * Does not guarantee that the archive file position is anything rational; * the TNode's fileposn should be (and is) used here. */ static void data_thread(arcfd, RNodePtr, TNodePtr) int arcfd; RNode *RNodePtr; TNode *TNodePtr; { long fileposn; /* absolute position of thread in file */ char *fn; int ov; if (print) /* this is something of a hack... */ if (TNodePtr->THptr->thread_kind != 0x0000) { /* not a data fork? */ fprintf(stderr, "Can't print non-data fork for '%s'.\n", RNodePtr->filename); return; /* this hoses the file posn... */ } else { if (verbose) printf("\n***** %s *****\n", RNodePtr->filename); ov = verbose; verbose = FALSE; /* turn off "unshrinking..." messages */ fileposn = RNodePtr->TNodePtr->fileposn; ExtractThread(arcfd,fileposn, "stdout", RNodePtr->TNodePtr->THptr); verbose = ov; return; } switch (TNodePtr->THptr->thread_kind) { case 0x0000: /* data fork */ if (verbose) { printf("Extracting '%s' (data)...", RNodePtr->filename); fflush(stdout); } /* create any needed subdirs */ fn = CreatePath(RNodePtr->filename, RNodePtr->RHptr->file_sys_info); /* extract the file */ fileposn = RNodePtr->TNodePtr->fileposn; if (ExtractThread(arcfd, fileposn, fn, RNodePtr->TNodePtr->THptr)) { SetFInfo(fn, RNodePtr->RHptr); /* set file attributes, dates... */ if (verbose) printf("done.\n"); } break; case 0x0001: /* disk image */ printf("Found disk image\n"); break; case 0x0002: /* resource_fork */ printf("Found resource_fork\n"); break; default: printf("Found unknown data_thread %.4x in '%s'\n", TNodePtr->THptr->thread_kind, RNodePtr->filename); break; } } /* * Extract files from archive * * Scan archive, extracting files which start with the strings in "names". * Calls subroutines to handle the various thread_class types. */ static void Extract(filename, namecount, names) char *filename; int namecount; char **names; { ListHdr *archive; int arcfd; /* archive file descriptor */ int rec, idx; MHblock *MHptr; /* Master Header block */ RNode *RNodePtr; /* Record Node */ TNode *TNodePtr; /* Thread block */ int len, *lentab; /* hold strlen() of all names */ char *pn; /* archived pathname */ int thread; /* current thread #; max 65535 threads */ BOOLEAN gotone = FALSE; static char *procName = "Extract"; archive = NuRead(filename); if ((arcfd = open(archive->arc_name, O_RDONLY | O_BINARY)) < 0) Fatal("Unable to open archive", procName); pakbuf = (onebyt *) Malloc(PAKBUFSIZ); /* allocate unpack buffer */ lentab = (int *) Malloc( sizeof(int) * namecount ); /* calloc() is nicer */ for (idx = 0; idx < namecount; idx++) /* calc. once (for efficiency) */ lentab[idx] = strlen(names[idx]); MHptr = archive->MHptr; RNodePtr = archive->RNodePtr; if (!namecount) extall = TRUE; /* main record read loop */ for (rec = 0; rec < MHptr->total_records; rec++) { pn = RNodePtr->filename; len = strlen(pn); if (RNodePtr->RHptr->version_number > MAXVERS) { printf("Unable to extract '%s': unknown record version_number\n", pn); continue; /* with for */ } for (idx = 0; extall || idx < namecount; idx++) { /* find arced file */ /* try to match argument with first few chars of stored filename */ /* or the entire filename, depending on EXPAND flag */ if (extall || ((len >= lentab[idx]) && doExpand ? (!strncasecmp(pn, names[idx], lentab[idx])) : (!strcasecmp(pn, names[idx])) )) { gotone = TRUE; /* go through all threads */ TNodePtr = RNodePtr->TNodePtr; for (thread = 0; thread < (int) RNodePtr->RHptr->total_threads; thread++) { switch(TNodePtr->THptr->thread_class) { case 0x0000: message_thread(arcfd, RNodePtr, TNodePtr); break; case 0x0001: control_thread(arcfd, RNodePtr, TNodePtr); break; case 0x0002: data_thread(arcfd, RNodePtr, TNodePtr); break; default: printf("Unknown thread_class %.4x for '%s'\n", TNodePtr->THptr->thread_class, RNodePtr->filename); break; } TNodePtr = TNodePtr->TNext; } break; /* out of filename matching (inner) FOR loop */ } } RNodePtr = RNodePtr->RNext; /* move on to next record */ } if (!gotone && verbose) printf("None selected\n"); if (close(arcfd) < 0) Fatal("Source (archive) close failed", procName); } /* * Entry point to extract routines. */ void NuExtract(filename, namecount, names, options) char *filename; int namecount; char **names; char *options; { static char *procName = "NuExtract"; if (*options == 'p') { /* printing rather then extracting to file */ print = TRUE; dopack = TRUE; /* no extract uncompressed! */ } else print = FALSE; Extract(filename, namecount, names); /* do stuff */ } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'numain.c'" '(14251 characters)' if test -f 'numain.c' then echo shar: "will not over-write existing file 'numain.c'" else cat << \!Funky!Stuff! > 'numain.c' /* * numain.c - shell-based front end for CShrink * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" /* system-dependent defines */ #include <stdio.h> /* standard I/O library */ #include <errno.h> #include <fcntl.h> #include <ctype.h> /* C type stuff, like tolower() */ #ifdef BSD43 # include <strings.h> #else /* SYSV, APW, MSC */ # include <string.h> /* string stuff */ #endif #ifdef APW # include <stdlib.h> # include <types.h> # include <strings.h> # include <shell.h> #endif #include "nuread.h" /* structs for archive info, archive read routines */ #include "nuview.h" /* archive listing functions */ #include "nuadd.h" /* archive operations (add, create, move) */ #include "nuext.h" /* archive operations (extract) */ #include "nupdel.h" /* archive operations (delete, update, freshen) */ #include "nublu.h" /* Binary II archive operations */ #include "nupak.h" /* need PAKBUFSIZ */ #include "nuetc.h" /* Malloc(), Fatal(), etc. */ extern char *getenv(); /* +PORT+ */ #define Whoops(str) printf("WARNING: typedef %s may be set incorrectly\n",str); #define ENVAR "NULIBOPT" /* environment variable with options in it */ /* * global to entire program */ int HiLo; /* byte ordering; FALSE on low-first (65816) */ int verbose; /* print verbose? */ int interact; /* interactive overwrite mode? */ int dopack; /* do we want to pack/unpack? */ int doExpand; /* do we want to expand archive filenames? */ int doSubdir; /* process subdirectories at all? */ int transfrom; /* how to do CR<->LF translation (from what?) (-1 = off) */ int transto; /* translate to ? */ int packMethod; /* how to pack a file */ fourbyt defFileType; /* default file type */ fourbyt defAuxType; /* default aux type */ onebyt *pakbuf; /* used by compression routines; created once to reduce */ /* overhead involved in malloc()ing a 64K buffer */ char *prgName = "NuLib"; /* for error messages; don't like argv[0] */ /* besides, the name changes every 3 weeks */ static char *header = "CShrink v2.1.1 November 1989 Freeware Copyright 1989 By Andy McFadden"; /* * Print simple usage info */ static void Usage(argv0) char *argv0; { printf("\nUsage: %s option[suboption] archive-name [filespec]\n", argv0); printf("\nType \"%s h\" for help.\n", argv0); } /* * Print usage info */ static void Help(argv0, options) char *argv0, *options; { if (INDEX(options+1, 'n')) { /* using 'n' suboption? */ printf("%s\n", header); printf("\nCompression methods:\n"); printf(" # Name Abbr Pack? Unpack?\n"); printf(" 0: Uncompressed unc Y Y\n"); printf(" 1: SQueezed (sq/usq) squ N Y\n"); printf(" 2: Dynamic LZW (ShrinkIt) shk * Y\n"); printf("The default is #2\n"); printf("\nText conversion methods:\n"); printf(" 0: From/to CR (ProDOS files)\n"); printf(" 1: From/to LF (UNIX files)\n"); printf(" 2: From/to CRLF (MS-DOS files)\n"); } else if (INDEX(options+1, 'w')) { /* print author info */ printf("%s\n", header); printf( "Internet: fadden@cory.berkeley.edu Usenet: ...!ucbvax!cory!fadden\n"); printf("\nShrinkIt and NuFX standard by Andy Nicholas.\n"); printf("ShrinkIt LZW compression by Kent Dickey\n"); printf( "Binary II unpack and unsqueeze C code adapted from unblu.c and usq.c by\n"); printf(" Marcel J.E. Mol (usq.c based on usq2/sq3 by Don Elton).\n"); printf("\nMS-DOS port by Robert B. Hess (roberth@microsof.uu.net).\n"); printf( "\nThanks go to all those who helped me test this, especially to the few\n"); printf(" who bothered to respond.\n"); printf( "\nThis program is Freeware. Please distribute as widely as possible, but\n"); printf( " don't sell it. Source code is available via e-mail upon request.\n"); printf( "\nUsers without Usenet/Internet access may send mail to me at:\n"); printf(" 1474 Saskatchewan Drive\n"); printf(" Sunnyvale, CA 94087\n"); } else { /* default help screen */ printf("%s\n", header); printf( "\nUsage: %s option[suboption] archive-name [filespec]\n", argv0); printf("Option must be one of:\n"); printf(" a[vucsrf] add to archive\n"); #ifndef NO_BLU printf(" b[xvti] Binary II archive operations\n"); #endif printf( " c[vucsrf] create archive (add, but suppress 'create' message)\n"); printf(" d[v+] delete file from archive\n"); printf(" f[vucsrf] freshen files in archive\n"); printf(" h[nw] show help screen\n"); printf(" i[v] verify archive integrity\n"); printf(" m[vucsrf] move files to archive (add, delete original)\n"); printf(" p[vt+] print archived file to stdout\n"); printf(" t[vz] display table of contents\n"); printf(" u[vucsrf] update files in archive\n"); printf(" v verbose listing (ProDOS 8 ShrinkIt format)\n"); printf(" x,e[vuti+] extract from archive\n"); printf("[ ShrinkIt compression partially implemented ]\n"); } } /* * Check machine dependencies */ static void CheckMach() { onebyt one; onebyt *oneptr; twobyt two; fourbyt four; #ifdef UNIX # ifdef APW ^^ "ERROR: You have both APW and UNIX defined" ^^ # endif # ifdef MSDOS ^^ "ERROR: You have both MSDOS and UNIX defined" ^^ # endif #endif /*UNIX*/ #ifdef APW # ifdef MSDOS ^^ "ERROR: You have both APW and MSDOS defined" ^^ # endif #endif /* some compilers complain about (unsigned) -1 , so I'm doing it this */ /* way to keep everybody quiet. */ one = 0x100; if (one) Whoops("onebyt"); /* one > 1 */ two = 0x10000; if (two) Whoops("twobyt"); /* two > 2 */ two = 0x1000; if (!two) Whoops("twobyt"); /* two < 2 */ four = 0xffffffff; four++; if (four) Whoops("fourbyt"); /* four > 4 */ four = 0x10000; if (!four) Whoops("fourbyt"); /* four < 4 */ /* check byte ordering */ two = 0x1122; oneptr = (onebyt *) &two; if (*oneptr == 0x11) HiLo = TRUE; else if (*oneptr == 0x22) HiLo = FALSE; else { printf("WARNING: Unable to determine a value for HiLo\n"); HiLo = FALSE; } /* check some other stuff... compilers may (should?) give warnings here */ if (ATTSIZE < MHsize) printf("WARNING: ATTSIZE must be >= than MHsize\n"); if (RECBUFSIZ < ATTSIZE) printf("WARNING: RECBUFSIZ should be larger than ATTSIZE\n"); if (MHsize != 48 || THsize != 16) printf("WARNING: Bad MHsize or THsize\n"); if (sizeof(Time) != 8) printf("WARNING: struct Time not 8 bytes\n"); } /* * Check to see if string 2 is in string 1. * * Returns the position of string 2 within string 1; -1 if not found. */ static int strc(host, sub) char *host, *sub; { int hlen = strlen(host); int slen = strlen(sub); int i; if (slen > hlen) /* substring longer than host string */ return (-1); /* generic linear search... */ for (i = 0; i <= (hlen - slen); i++) if ((*(host+i) == *sub) && (!strncmp(host+i, sub, slen))) return (i); return (-1); } /* * Yank a number from a character string. */ int OptNum(ptr) char *ptr; { int val = 0; while (*ptr && isdigit(*ptr)) { val *= 10; val += (*ptr - '0'); ptr++; } return (val); } /* * Set default values for globals. * * Should be of form "NULIBOPT=..." * verbose : default to verbose output * interactive : default to interactive mode when overwriting * type=xxx : set storage type to ProDOS type "xxx" * aux=xxxx : set aux storage type to 4-byte hex number "xxxx" */ void GetDefaults(options) char *options; { char *envptr; int off, idx, pt; int len = strlen(options); char type[5]; /* set program default values */ verbose = FALSE; /* silent mode */ interact = FALSE; /* don't ask questions */ doSubdir = TRUE; /* process subdirectories unless told not to */ dopack = TRUE; /* don't pack unless told to */ doExpand = FALSE; /* don't expand archived filenames */ packMethod = 0x0002;/* ShrinkIt LZW */ transfrom = -1; /* no text translation */ transto = -1; defFileType = (fourbyt) 0; /* NON */ defAuxType = (fourbyt) 0; /* $0000 */ /* read from global envir var */ if (envptr = getenv(ENVAR)) { if (strc(envptr, "verbose") >= 0) { verbose = TRUE; } if (strc(envptr, "interactive") >= 0) { interact = TRUE; } if ((off = strc(envptr, "type=")) >= 0) { off += 5; if (off+3 > strlen(envptr)) { fprintf(stderr, "Error with 'type=xxx' in NULIBOPT var\n"); Quit (-1); } strncpy(type, envptr+off, 3); type[3] = '\0'; for (idx = 0; idx < 256; idx++) /* scan for file type */ if (!strcasecmp(FT[idx], type)) { defFileType = (fourbyt) idx; break; /* out of for */ } } if ((off = strc(envptr, "aux=")) >= 0) { off += 4; if (off+4 > strlen(envptr)) { fprintf(stderr, "Error with 'aux=$xxxx' in NULIBOPT var\n"); Quit (-1); } strncpy(type, envptr+off, 4); type[4] = '\0'; sscanf(type, "%x", &defAuxType); } } /* handle command line suboption string */ for (pt = 1; pt < len; pt++) { /* skip option char */ switch(options[pt]) { case '+': /* expand */ doExpand = TRUE; break; case 'c': /* compress method */ packMethod = OptNum(&options[pt+1]); while (pt < len && isdigit(options[pt+1])) /* advance to next */ pt++; dopack = TRUE; break; case 'f': /* filetype specified */ strncpy(type, &options[pt+1], 3); type[3] = '\0'; for (idx = 0; idx < 256; idx++) /* scan for file type */ if (!strcasecmp(FT[idx], type)) { defFileType = (fourbyt) idx; break; /* out of for */ } pt += strlen(type); if (options[pt+1] == '/') { /* auxtype specification */ pt++; strncpy(type, &options[pt+1], 4); type[4] = '\0'; sscanf(type, "%lx", &defAuxType); pt += strlen(type); } break; case 'i': /* interactive overwrites */ interact = TRUE; break; case 'n': /* help with numbers */ /* do nothing */ break; case 'r': /* don't recursively descend subdir */ doSubdir = FALSE; break; case 's': /* store method */ packMethod = OptNum(&options[pt+1]); while (pt < len && isdigit(options[pt+1])) /* advance to next */ pt++; dopack = FALSE; break; case 't': /* how to translate text? */ transfrom = OptNum(&options[pt+1]); while (pt < len && isdigit(options[pt+1])) pt++; break; case 'u': /* don't use compression */ dopack = FALSE; /* this doesn't matter, but FALSE may be faster */ packMethod = 0x0000; /* archive w/o compression */ break; case 'v': /* verbose mode */ verbose = TRUE; break; case 'w': /* help on people */ /* do nothing */ break; case 'x': /* extract BLU files */ /* do nothing */ break; default: /* unknown */ printf("unknown subopt '%c'\n", options[pt]); break; /* do nothing */ } } /* for */ } #ifdef APW /* * Expand a ProDOS filename using APW wildcard calls (even if the file doesn't * exist). * * Returns a pointer to a buffer holding the filename. */ char *ExpandFilename(filename) char *filename; { char *ptr; c2pstr(filename); if (!(*filename)) { printf("Internal error: can't expand null filename\n"); Quit (-1); } INIT_WILDCARD(filename, 0); ToolErrChk(); p2cstr(filename); NEXT_WILDCARD(tmpNameBuf); p2cstr(tmpNameBuf); if (strlen(tmpNameBuf)) /* found it */ return(tmpNameBuf); else { /* file does not exist; expand path */ strcpy(tmpNameBuf, filename); ptr = RINDEX(tmpNameBuf, '/'); /* remove filename */ if (!ptr) /* filename only */ return (filename); *ptr = '\0'; if (!strlen(tmpNameBuf)) { /* something weird... */ printf("Unable to expand '%s'\n", filename); Quit (-1); } c2pstr(tmpNameBuf); INIT_WILDCARD(tmpNameBuf, 0); ToolErrChk(); NEXT_WILDCARD(tmpNameBuf); p2cstr(tmpNameBuf); if (!strlen(tmpNameBuf)) { printf("Unable to fully expand '%s'\n", filename); Quit (-1); } strcat(tmpNameBuf, RINDEX(filename, '/')); return (tmpNameBuf); } } #endif /* APW */ /* * Parse args, call functions. */ main(argc, argv) int argc; char **argv; { char *filename; /* hold expanded archive file name */ int idx; filename = (char *) Malloc(MAXFILENAME); CheckMach(); /* check compiler options, and set HiLo */ if (argc < 2) { /* no arguments supplied */ Usage(argv[0]); Quit (0); } if (argc < 3) { /* no archive file specified; show help screen */ if (argv[1][0] == 'h') /* could be HN or HW */ Help(argv[0], argv[1]); else /* not 'H' option; show generic help scrn */ Help(argv[0], "h"); Quit (0); } #ifdef APW strcpy(filename, ExpandFilename(argv[2])); #else strcpy(filename, argv[2]); #endif if (argv[1][0] == '-') { /* skip initial dashes */ argv[1]++; } for (idx = 0; argv[1][idx]; idx++) /* conv opts to lower case */ if (isupper(argv[1][idx])) argv[1][idx] = tolower(argv[1][idx]); GetDefaults(argv[1]); /* get defaults, process suboption string */ pakbuf = (onebyt *) Malloc(PAKBUFSIZ); /* allocate global pack buf */ switch (argv[1][0]) { case 'a': /* add */ case 'c': /* create */ case 'm': /* move */ NuAdd(filename, argc-3, argv+3, argv[1]); /* NuAdd will read */ break; #ifndef NO_BLU case 'b': /* Binary II operations */ NuBNY(filename, argc-3, argv+3, argv[1]); break; #endif case 'd': /* delete */ NuDelete(filename, argc-3, argv+3, argv[1]); break; case 'f': /* freshen */ case 'u': /* update */ NuUpdate(filename, argc-3, argv+3, argv[1]); break; case 'i': /* verify integrity */ NuTest(filename, argv[1]); break; case 't': /* table of contents */ case 'v': /* verbose output */ NuView(filename, argv[1]); break; case 'e': /* extract */ case 'x': case 'p': NuExtract(filename, argc-3, argv+3, argv[1]); break; default: /* need help */ fprintf(stderr, "%s: unknown option '%c'\n", argv[0], argv[1][0]); break; } free (filename); free (pakbuf); Quit (0); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nupak.c'" '(10049 characters)' if test -f 'nupak.c' then echo shar: "will not over-write existing file 'nupak.c'" else cat << \!Funky!Stuff! > 'nupak.c' /* * nupak.c - interface to the compression routines * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 Novmber 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include <stdio.h> #include <fcntl.h> #ifdef MSDOS /* for file I/O */ # include <io.h> # include <sys\types.h> # include <sys\stat.h> # include <errno.h> #endif #include "nupak.h" #include "nuetc.h" #define CONVSIZ 1024 long packedSize; /* global - size of file after packing */ onebyt lastseen; /* used in crlf(); must be set by caller */ /* * Make a little spinning thing. * * This just lets the user know that the whole thing hasn't stalled on him. * Prints a character, then backspaces so that the next thing will overwrite * it. * * Currently called by FCopy(), unpak_SHK(), pak_SHK() */ void Spin() { static char *sp = "/-\\|"; static int posn = 0; posn++; if ((posn < 0) || (posn > 3)) posn = 0; putchar(sp[posn]); putchar('\b'); fflush(stdout); } /* * Convert the end-of-line terminator between systems. * * Compile-time defines determine the value that things are translated to; * the value of "translate" determines what they are translated from. This * will write the contents of the buffer to the passed file descriptor, * altering bytes as necessary. Max buffer size is 64K. Note that the * syntax is the same as for write(); * * This would have been a lot easier without IBM... lastseen is the last * character seen (used only in CRLF translations). This needs to be set * by the caller (FCopy(), extract_files()). * * The putc_ncr() procedure in nusq.c does its own processing; this was * somewhat unavoidable. * * BUGS: This proc will have to be re-written. It would be nice to be * able to pack files with a CRLF translation, not just unpack... but you * can't just do buffer writes for that. It'll take some work, and will * probably appear in the next version. */ unsigned int crlf(dstfd, buffer, length) int dstfd; onebyt *buffer; unsigned int length; { register BOOLEAN doconv; register onebyt *bptr = buffer; register unsigned int idx; static char *procName = "crlf"; unsigned int partial; /* size for partial read/write */ onebyt tobuf[2048]; onebyt *toptr; int conv; unsigned int origlength = length; if ((transfrom == -1) && (transto == -1)) { /* no translation necessary */ return (write(dstfd, buffer, length)); } if (transfrom < -1 || transfrom > 2) { fprintf(stderr, "%s: unknown translation type %d\n", prgName, transfrom); fprintf(stderr, "%s: assuming conversion 0 (from CR)\n", prgName); transfrom = 0; } if (transto < -1 || transto > 2) { fprintf(stderr, "%s: unknown translation type %d\n", prgName, transto); fprintf(stderr, "%s: assuming conversion 0 (to CR)\n", prgName); transto = 0; } /* macro defs for system-dependent actions */ #ifdef UNIX # define DEFCONVFROM if (*bptr == 0x0a) /* LF */ \ doconv = TRUE # define DEFCONVTO *(toptr++) = 0x0a #else # ifdef APW # define DEFCONVFROM if (*bptr == 0x0d) /* CR */ \ doconv = TRUE # define DEFCONVTO *(toptr++) = 0x0d # endif # ifdef MSDOS # define DEFCONVFROM if ((*bptr == 0x0a) && (lastseen == 0x0d)) { \ doconv = TRUE; toptr--; /*already outputed CR; back up over it*/ } lastseen = *bptr # define DEFCONVTO *(toptr++) = 0x0d; \ *(toptr++) = 0x0a # endif # ifndef APW # ifndef MSDOS # define DEFCONVFROM if (*bptr == 0x0a) /* LF */ \ doconv = TRUE # endif /* none2 */ # endif /* none1 */ #endif /* UNIX */ while (length != 0) { if (length > CONVSIZ) { partial = CONVSIZ; length -= CONVSIZ; } else { partial = length; length = 0; } /* uses an explicit flag rather than "continue" for clarity... */ toptr = tobuf; for (idx = partial; idx > 0; idx--, bptr++) { doconv = FALSE; switch (transfrom) { case -1: /* convert from current system's terminator */ DEFCONVFROM; break; case 0: if (*bptr == 0x0d) /* CR */ doconv = TRUE; break; case 1: if (*bptr == 0x0a) /* LF */ doconv = TRUE; break; case 2: if ((*bptr == 0x0a) && (lastseen == 0x0d)) { doconv = TRUE; toptr--; /*already outputed CR; back up over it*/ } lastseen = *bptr; break; } if (doconv) { switch (transto) { case -1: /* convert to current system's terminator */ DEFCONVTO; break; case 0: *(toptr++) = 0x0d; break; case 1: *(toptr++) = 0x0a; break; case 2: *(toptr++) = 0x0d; *(toptr++) = 0x0a; break; } } else { *(toptr++) = *bptr; } } /* for loop */ if (write(dstfd, tobuf, (toptr-tobuf)) != (toptr-tobuf)) Fatal("Dest write failed", procName); } /* while loop */ return (origlength); } /* * Read a file, and place in another file at current posn. We can't read more * than PAKBUFSIZ at a time, so for some files it will have to be broken down * into pieces. Note PAKBUFSIZ is expected to be an int (defined in nupak.h), * and can't be any larger than read() can handle (64K... unsigned 16-bit int). * * The transl option is present for NuUpdate and NuDelete, which have to * copy old records to a new archive w/o performing translation. */ void FCopy(srcfd, dstfd, length, copybuf, transl) int srcfd; /* source file descriptor (must be open & seek()ed) */ int dstfd; /* destination file descriptor (must be open & seek()ed) */ fourbyt length; /* number of bytes to copy */ onebyt *copybuf; BOOLEAN transl; /* maybe do text translation? */ { unsigned int partial; /* size for partial read/write */ static char *procName = "FCopy"; if (transl) lastseen = '\0'; while (length != 0L) { if (length > (long) PAKBUFSIZ) { partial = (unsigned int) PAKBUFSIZ; length -= (long) PAKBUFSIZ; if (verbose) Spin(); } else { partial = (unsigned int) length; length = 0L; } if (read(srcfd, copybuf, partial) != partial) Fatal("Source read failed", procName); if (transl) { /* do text translation if user wants it */ if (crlf(dstfd, copybuf, partial) != partial) Fatal("Dest write failed (c)", procName); } else { /* NEVER do translation */ if (write(dstfd, copybuf, partial) != partial) Fatal("Dest write failed (w)", procName); } } } /* * Add a range of bytes from one file into another, packing them. * * Set up stuff, then call the appropriate pack routine. Returns the actual * algorithm used (thread_format), since the compression algorithm could * fail, storing the file in uncompressed format instead. The packed length * is stored in a global variable. */ twobyt PackFile(srcfd, dstfd, thread_eof, thread_format, buffer) int srcfd; /* source file descriptor (must be open & seek()ed) */ int dstfd; /* destination file descriptor (must be open & seek()ed) */ fourbyt thread_eof; /* size of input */ int thread_format; /* how to pack the bytes */ onebyt *buffer; /* alloc in main prog so we don't have to each time */ { long length = (long) thread_eof; static char *procName = "PackFile"; twobyt retval = thread_format; /* default = successful pack */ switch (thread_format) { case 0x0000: /* uncompressed */ if (verbose) { printf("storing...", thread_format); fflush(stdout); } FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; break; case 0x0001: /* SQUeeze */ if (verbose) { printf("[can't squeeze; storing]..."); fflush(stdout); } else { printf("WARNING: can't squeeze; files stored uncompressed\n"); } FCopy(srcfd, dstfd, length, buffer, TRUE); packedSize = length; retval = 0x0000; /* uncompressed */ break; case 0x0002: /* LZW (ShrinkIt) */ if (verbose) { printf("shrinking..."); fflush(stdout); } /* packedSize set by pak_SHK */ retval = pak_SHK(srcfd, dstfd, length, buffer); break; default: fprintf(stderr, "\nUnknown compression method %d\n", thread_format); fprintf(stderr, "Aborting.\n"); Quit(-1); } return (retval); } /* * Extract a range of bytes from one file into another, unpacking them. * * Set up stuff, then call the appropriate unpack routine. Leaves the srcfd * positioned past the data to be unpacked; the calling routine should not * have to do any seeks. * * Returns TRUE if able to unpack, FALSE if not able to. Note that srcfd * WILL be seeked even if the compression method is not handled. */ int UnpackFile (srcfd, dstfd, comp_thread_eof, thread_eof, thread_format, buffer) int srcfd; /* source file descriptor (must be open & seek()ed) */ int dstfd; /* destination file descriptor (must be open & seek()ed) */ fourbyt comp_thread_eof; /* number of bytes in source */ fourbyt thread_eof; /* number of bytes to output */ int thread_format; /* how to unpack the bytes */ onebyt *buffer; { long length = (long) comp_thread_eof; static char *procName = "UnpackFile"; BOOLEAN retval = TRUE; /* default to success */ switch (thread_format) { case 0x0000: /* uncompressed */ if (verbose) { printf("extracting...", thread_format); fflush(stdout);} FCopy(srcfd, dstfd, length, buffer, TRUE); break; case 0x0001: /* unSQUeeze */ #ifdef NO_BLU if (verbose) { printf("[can't unsqueeze - aborting]..."); fflush(stdout); } else { printf("ERROR: can't unsqueeze; 'squ' files not extracted\n"); } lseek(srcfd, length, S_REL); /* set file posn */ retval = FALSE; #else if (verbose) { printf("unsqueezing..."); fflush(stdout); } unpak_SQU(srcfd, dstfd, length); /* thread_eof not needed */ #endif break; case 0x0002: /* LZW (ShrinkIt) */ if (verbose) { printf("unshrinking..."); fflush(stdout); } unpak_SHK(srcfd, dstfd, comp_thread_eof, thread_eof, buffer); break; default: fprintf(stderr, "Unknown uncompression method %d\n", thread_format); lseek(srcfd, length, S_REL); /* set file posn */ retval = FALSE; break; } return (retval); } !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive
fadden@cory.Berkeley.EDU (Andy McFadden) (11/09/89)
NuLib part 4/5 ----- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # nupdel.c # nuread.c # nushk.c # This archive created: Thu Nov 9 01:07:41 1989 # By: Andy McFadden () export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'nupdel.c'" '(13337 characters)' if test -f 'nupdel.c' then echo shar: "will not over-write existing file 'nupdel.c'" else cat << \!Funky!Stuff! > 'nupdel.c' /* * nudel.c - operations which delete from a NuFX archive * nuupd.c - update/freshen a NuFX archive * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include <stdio.h> #include <fcntl.h> #ifdef BSD43 # include <strings.h> #else /* SYSV, APW, MSC */ # include <string.h> #endif #ifdef UNIX # include <errno.h> #endif #ifdef APW # include <types.h> # include <prodos.h> /* ? */ # include <shell.h> # include <strings.h> /* APW string ops */ #endif #ifdef MSDOS # include <io.h> # include <sys\types.h> # include <sys\stat.h> # include <errno.h> #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; #ifdef APW FileRec create_p; #endif static char *procName = "RebuildArchive"; if (verbose) { printf("Building new archive file..."); fflush(stdout); } mptr = MakeMHblock(archive, remaining); /* build master header */ ArcfiCreate(tmpname); /* create file */ if ((dstfd = open(tmpname, O_WRONLY | O_TRUNC | O_BINARY, WPERMS)) < 0) Fatal("Unable to open dest file", procName); if (write(dstfd, mptr, MHsize) < MHsize) Fatal("Unable to write master header", 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->namelen; 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, size, S_REL) < 0) Fatal("Unable to seek past deleted record", procName); } else { FCopy(arcfd, dstfd, size, pakbuf, FALSE); } RNodePtr = RNodePtr->RNext; /* move on to next record */ } if (close(dstfd) < 0) Fatal("Unable to close archive", procName); if (verbose) printf("done.\n"); } /* * Delete files from archive * * Scan archive, deleting files which start with 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, (long) 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; 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 dates; avoid month/year stuff by straight compare */ if ((atptr->year > ftptr->year) || (atptr->month > ftptr->month)) keeparc = TRUE; else if ((atptr->year < ftptr->year) || (atptr->month < atptr->month)) 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); /* build master header */ mptr = MakeMHblock(archive, totalrecs); if (lseek(arcfd, (long) 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 (write(dstfd, mptr, MHsize) < MHsize) Fatal("Unable to write master header", procName); RNodePtr = archive->RNodePtr; for (rec = 0; rec < MHptr->total_records; rec++) { size = (long) RNodePtr->RHptr->attrib_count; size += (long) RNodePtr->namelen; 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); /* if (verbose) printf("done.\n"); */ } else { /* file replaced; skip orig and add new */ if (lseek(arcfd, size, S_REL) < 0) Fatal("Bad skip seek", procName); twoptr = (twobyt *) RNodePtr->filename; idx = *twoptr; if (verbose) printf("Replacing/"); /* +"Adding 'filename'..." */ 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) { AddFile(dstfd, FIArray[idx]); } } } 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 <subopt> */ if (transfrom >= 0) { transto = transfrom; transfrom = -1; } archive = NuRead(filename); Update(archive, namecount, names); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nuread.c'" '(14376 characters)' if test -f 'nuread.c' then echo shar: "will not over-write existing file 'nuread.c'" else cat << \!Funky!Stuff! > 'nuread.c' /* * nuread.c - read NuFX archives (header info only) into structures * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include <stdio.h> #include <fcntl.h> #include <errno.h> #ifdef MSDOS /* For file IO */ # include <string.h> # include <io.h> # include <sys\types.h> # include <sys\stat.h> #endif #ifdef CRC_TAB # include "crc.h" /* fast CRC lookup */ #endif #include "nuread.h" #include "nupak.h" /* uses PAKBUFSIZ */ #include "nuetc.h" /* quick proc to save x00 bytes of static storage */ void OtherArc(str1, str2) { fprintf(stderr, "File may be %s; try \"%s\".\n", str1, str2); } /* swap two bytes if HiLo is TRUE */ void HiSwap(ptr, a, b) onebyt *ptr; register onebyt a, b; { register onebyt tmp; if (HiLo) { tmp = ptr[a], ptr[a] = ptr[b], ptr[b] = tmp; } } /* copy bytes from buffer to buffer, reversing byte order if necessary */ void BCopy(srcptr, destptr, num, order) onebyt *srcptr, *destptr; register int num; BOOLEAN order; /* true if byte ordering is important */ { register int i = num--; if (order && HiLo) { while (i--) { /* copy & reverse */ *(destptr+i) = *(srcptr + num - i); /* dest+3 = src + 3 - 3 .. */ } } else if (order) { while (i--) { /* copy only */ *(destptr+i) = *(srcptr + i); } } else { while (i--) { /* byte ordering not important; just copy */ *(destptr+i) = *(srcptr+i); } } } /* * Calculate CRC on a region * * A CRC is the result of a mathematical operation based on the * coefficients of a polynomial when multiplied by X^16 then divided by * the generator polynomial (X^16 + X^12 + X^5 + 1) using modulo two * arithmetic. * * This routine is a slightly modified verison of one found in: * _Advanced Programming Techniques for the Apple //gs Toolbox_ * By Morgan Davis and Dan Gookin (Compute! Publications, Inc.) * It can either calculate the CRC bit-by-bit or use a table. * [ one of the few //gs books worth the money +atm ] */ twobyt CalcCRC(seed, ptr, count) twobyt seed; /* initial value for CRC */ onebyt *ptr; /* pointer to start of data buffer */ int count; /* number of bytes to scan through - note 64K max */ { register int x; register twobyt CRC = seed; do { #ifndef CRC_TAB CRC ^= *ptr++ << 8; /* XOR hi-byte of CRC w/data */ for (x = 8; x; --x) /* Then, for 8 bit shifts... */ if (CRC & 0x8000) /* Test hi order bit of CRC */ CRC = CRC << 1 ^ 0x1021; /* if set, shift & XOR w/$1021 */ else CRC <<= 1; /* Else, just shift left once. */ #else CRC = updcrc(*ptr++, CRC); /* look up new value in table */ #endif } while (--count); return (CRC); } /* * Test an archive's integrity. * * If the verbose setting is used, it prints CRCs for entire records (headers, * threads, etc). */ 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 size; int srcfd; /* file descriptor */ int thread; twobyt CRC; long CRCsum = 0L; static char *procName = "NuTest"; printf("Testing %s", filename); if (verbose) printf("\n"); else { printf("..."); fflush(stdout); } archive = NuRead(filename); /* major glitches cause this to fail */ MHptr = archive->MHptr; RNodePtr = archive->RNodePtr; copybuf = (onebyt *) Malloc(PAKBUFSIZ); if ((srcfd = open(filename, O_RDONLY | O_BINARY)) < 0) Fatal("Unable to close archive", procName); if (lseek(srcfd, (long) MHsize, S_ABS) < 0) /* seek past master block */ Fatal("Bad seek (MH)", procName); for (rec = 0; rec < (unsigned int) MHptr->total_records; rec++) { if (verbose) printf("Record %d (%s): ", rec, RNodePtr->filename); size = (long) RNodePtr->RHptr->attrib_count; size += (long) RNodePtr->namelen; 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 (verbose) { printf("record size = %ld, ", size); fflush(stdout); } CRC = 0; while (size != 0L) { if (size > (long) PAKBUFSIZ) { partial = (unsigned int) PAKBUFSIZ; size -= (long) PAKBUFSIZ; } else { partial = (unsigned int) size; 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) CRC = CalcCRC(CRC, copybuf, partial); if (verbose) printf("CRC = $%.4x\n", CRC); if (verbose) CRCsum += CRC; } 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, tu_ptr, tc_ptr, CRC_ptr) int fd; RHblock *RHptr; fourbyt *tu_ptr, /* pointer to int holding len of uncomp. threads */ *tc_ptr; /* same for sum of comp_thread_eof */ 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"; *tu_ptr = 0L, *tc_ptr = 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); perror("ReadThread (THblock)"); } CRC = CalcCRC(CRC, filebuf, 16); /* rec 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->reserved, 2, TRUE); BCopy(filebuf+8, (onebyt *) &THptr->thread_eof, 4, TRUE); BCopy(filebuf+12, (onebyt *) &THptr->comp_thread_eof, 4, TRUE); /* update pointers and skip the actual data */ *tu_ptr += THptr->thread_eof; *tc_ptr += THptr->comp_thread_eof; if ((TNodePtr->fileposn = lseek(fd, 0L, S_REL)) < 0) Fatal("Bad thread posn lseek()", procName); if (THptr->comp_thread_eof > 2097152L) /* SANITY CHECK */ fprintf(stderr, "Sanity check: found comp_thread_eof > 2MB\n"); if (lseek(fd, (long) THptr->comp_thread_eof, S_REL) < 0) Fatal("Bad seek", procName); } *CRC_ptr = CRC; return (THeadPtr); } /* * Read header data from a NuFX archive into memory */ ListHdr *NuRead(filename) char *filename; { int fd; /* archive file descriptor */ char namebuf[MAXFILENAME]; int rec, num; BOOLEAN first; twobyt namelen; twobyt CRC; ListHdr *ListPtr; /* List Header struct */ MHblock *MHptr; /* Master Header block */ RNode *RNodePtr; /* Record Node */ RHblock *RHptr; /* Record Header block */ onebyt filebuf[RECBUFSIZ]; /* must be > RH, MH, or atts-RH size */ static char *procName = "NuRead"; ListPtr = (ListHdr *) Malloc(sizeof(ListHdr)); /* create head of list */ ListPtr->arc_name = (char *) Malloc(strlen(filename)+1); /* archive fnam */ strcpy(ListPtr->arc_name, filename); ListPtr->MHptr = (MHblock *) Malloc(sizeof(MHblock)); /* master block */ if ((fd = open(filename, O_RDONLY | O_BINARY)) < 0) { if (errno == ENOENT) { fprintf(stderr, "%s: can't find file '%s'\n", prgName, filename); Quit (-1); } else Fatal("Unable to open archive", procName); } /* create and read the master header block */ MHptr = ListPtr->MHptr; if (read(fd, filebuf, MHsize) < MHsize) { fprintf(stderr, "File '%s' may not be a NuFX archive\n", filename); Fatal("Unable to read Master Header Block", procName); } CRC = CalcCRC(0, filebuf+8, MHsize-8); /* calc 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->reserved, 20, 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, "SIT!", 4)) /* StuffIt */ OtherArc("a StuffIt archive", "StuffIt (Macintosh)"); if (!strncmp((char *) filebuf, "<ar>", 4)) /* system V arch */ OtherArc("a library archive (Sys V)", "ar"); if (!strncmp((char *) filebuf, "!<arch>", 7)) OtherArc("a library archive", "ar"); if (!strncmp((char *) filebuf, "#! /bin/sh", 10) || !strncmp((char *) filebuf, "#!/bin/sh", 9)) OtherArc("a shar archive", "/bin/sh"); if (!strncmp((char *) filebuf, "GIF87a", 6)) OtherArc("a GIF picture", "?!?"); /* still need ARC and ZOO */ Quit (-1); } if (CRC != MHptr->master_crc) printf("WARNING: Master Header block may be corrupted (bad CRC)\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 */ 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); } CRC = CalcCRC(0, filebuf+6, RHsize-6); /* rec hdr CRC part 1/5 */ 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, 4, TRUE); BCopy(filebuf+14, (onebyt *) &RHptr->file_sys_id, 2, TRUE); BCopy(filebuf+16, (onebyt *) &RHptr->file_sys_info, 2, 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); if (strncmp(RHptr->ID, RecordID, 4)) { fprintf(stderr, "Found bad record ID (#%d)\n", 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, filebuf, num); /* rec 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(&namelen, 0, 1); /* read filename, and store in struct */ if (namelen > MAXFILENAME) { fprintf(stderr, "ERROR: namelen > MAXFILENAME\n"); Quit (-1); } RNodePtr->namelen = namelen; if (read(fd, namebuf, namelen) < namelen) Fatal("Bad namebuf read", procName); CRC = CalcCRC(CRC, namebuf, namelen); /* rec hdr CRC part 4/5 */ RNodePtr->filename = (char *) Malloc(namelen+1); /* store fname */ BCopy(namebuf, (onebyt *) RNodePtr->filename, namelen, FALSE); RNodePtr->filename[namelen] = '\0'; RNodePtr->TNodePtr = ReadThreads(fd, RHptr, &RNodePtr->unc_len, &RNodePtr->comp_len, &CRC); /* rec hdr CRC part 5/5 calculated by ReadThreads */ if (CRC != RHptr->header_crc) { printf("WARNING: Detected a bad record header CRC\n"); printf(" Rec %d in file '%.60s'\n",rec,RNodePtr->filename); } } /* begin adding new files at this point */ if ((ListPtr->nextposn = lseek(fd, 0L, S_REL)) < 0) Fatal("Bad final lseek()", procName); if (close(fd) < 0) { Fatal("Bad close", procName); } return (ListPtr); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'nushk.c'" '(16430 characters)' if test -f 'nushk.c' then echo shar: "will not over-write existing file 'nushk.c'" else cat << \!Funky!Stuff! > 'nushk.c' /* * nushk.c - P8 ShrinkIt compress/uncompress routines * * By Andy McFadden (fadden@cory.berkeley.edu) * NuLib v2.1 November 1989 Freeware (distribute, don't sell) */ #include "nudefs.h" #include <stdio.h> #include <fcntl.h> #ifdef MSDOS /* For file IO */ # include <io.h> # include <sys\types.h> # include <sys\stat.h> # include <errno.h> #endif #include "nuread.h" /* need CalcCRC() */ #include "nupak.h" #include "nuetc.h" #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+1]; /* temporary buffer for storing data after LZW */ onebyt rbuf[BLKSIZ+1]; /* temporary buffer for storing data after RLE */ /* * Fake ShrinkIt compression routines. * * Only removes repeated characters; doesn't actually do the LZW. This * means that the compression achieved will not be all that great (if it * compresses at all). */ #define ESCAPE_CHAR 0xdb /* * 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 <DLE> char count */ int do_RLE(srcptr, dstptr) onebyt *srcptr, *dstptr; { int found, scount, dcount; onebyt c, lastc, tlastc; 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); } /* * Main entry point. */ long pak_SHK(srcfd, dstfd, length, copybuf) int srcfd, dstfd; long length; onebyt *copybuf; { unsigned int partial; /* size for partial read/write */ onebyt *rptr; register int idx; onebyt scratch[8]; long startposn, endposn; long comp_len = 0L; twobyt CRC; int rlesize; /* length after RLE */ int sc; /* spin counter */ static char *procName = "pak_SHK"; CRC = 0; if ((startposn = lseek(dstfd, 0L, S_REL)) < 0) Fatal("Bad seek (first)", procName); lseek(dstfd, 4L, 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); } CRC = CalcCRC(CRC, copybuf, BLKSIZ); /* calc on all 4096 bytes */ 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; } scratch[0] = (onebyt) (rlesize & 0x00ff); scratch[1] = (onebyt) ((rlesize >> 8) & 0x00ff); scratch[2] = 0; /* LZW off */ if (write(dstfd, scratch, 3) != 3) Fatal("Dest hdr write failed", procName); comp_len += 3; if (write(dstfd, rptr, rlesize) != rlesize) /* need to do CRLF */ Fatal("Dest write failed", procName); comp_len += rlesize; sc++; if (sc == 15) { sc = 0; Spin(); } } while (length != 0L); if ((endposn = lseek(dstfd, 0L, S_REL)) < 0) Fatal("Bad seek (now)", procName); if (lseek(dstfd, 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, 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 */ } /* * P8 ShrinkIt uncompression routines * * Copyright 1989 Kent Dickey * C translation by Kent Dickey / Andy McFadden */ static int inf; /* to make Getc() calls happy */ 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[16] = { 0x0000, 0x01ff, 0x03ff, 0x03ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff }; static int Number[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; static onebyt Stack[100]; /* simulated stack; should be <= 64 */ static int out_bytes, stack_ptr, entry, at_bit, at_byte; static onebyt last_byte; /* used in get_code */ /* fake Getc(); easier to make this a macro than to change the code */ #ifdef DEBUG onebyt Getc(foo) int foo; /* this is ignored */ { return (*(ibuf++)); } #else /* if not debugging, use a macro */ # define Getc(foo) *(ibuf++) #endif /* DEBUG */ /* * Stack operations; used by undo_LZW */ #ifdef DEBUG void push(a_byte) unsigned char a_byte; { if (stack_ptr > 100) { printf("\n*** stack_ptr exceeded 100 in push() [%d]\n", stack_ptr); exit (-1); } Stack[stack_ptr++] = a_byte; } #else /* if not debugging, use a macro */ # define push(a_byte) Stack[stack_ptr++] = a_byte #endif /* DEBUG */ #ifdef DEBUG void dump_stack(buffer) unsigned char *buffer; { printf("--- Going to dump stack, stack_ptr = %d, out_bytes = %d\n", stack_ptr, out_bytes); while (stack_ptr > 0) { *(buffer + out_bytes++) = Stack[--stack_ptr]; } } #else /* if not debugging, use a macro */ # define dump_stack(buffer) while (stack_ptr > 0) { \ *( buffer +out_bytes++) = Stack[--stack_ptr];\ } #endif /* DEBUG */ /* * Decipher LZW codes. */ static int get_code(/*Buffer*/) /*unsigned char *Buffer;*/ { int num_bits, old_bit, last_bit; long value, mask; unsigned char byte1, byte2, byte3; /* get compressed chars... */ #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; 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(inf); byte1 = last_byte; /* first byte = last one used */ byte2 = Getc(inf); if (last_bit > 16) { /* get 3rd byte if nec. */ byte3 = Getc(inf); last_byte = byte3; } else { byte3 = 0; last_byte = byte2; } value = ((((long)byte3 << 8) + (long)byte2) << 8) + (long)byte1; /* value = (((Buffer[at_byte+2] << 8) + Buffer[at_byte+1]) << 8) + */ /* Buffer[at_byte]; */ 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 if (old_bit) return ((value >> old_bit) & mask); else return (value & mask); /* shifting by zero may be undefined */ } /* * Un-LZW a range of bytes * * Reads data with get_code (eventually from packBuffer) and stores the * output in "buffer". */ static void undo_LZW(buffer, length) unsigned char *buffer; /* where to put output */ int length; /* uncompressed length of output */ { int oldc, incode, finalc, ptr; /* initialize variables */ Table = Real_tab-256; entry = 0x101; /* start at $101 */ at_bit = at_byte = 0; out_bytes = 0; stack_ptr = 0; last_byte = 0; /* init last_byte */ oldc = incode = get_code(/*buffer*/); finalc = (oldc & 0xff); *(buffer + out_bytes++) = (unsigned char) incode; /* main loop */ while (out_bytes < length) { incode = ptr = get_code(/*buffer*/); 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); 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 * <DLE> char count * which is different from the norm. */ static void undo_RLE(inbuffer, outbuffer) unsigned char *inbuffer, *outbuffer; /*int length; /* how many bytes from LZW; just to make sure... */ { int total, count; /* count is RLE reps */ unsigned char c; #ifdef DEBUG printf("Starting undo_RLE, length = %d\n", length); #endif total = 0; while (total < BLKSIZ) { c = *(inbuffer++); /*length--;*/ if (c == (onebyt) escape_char) { c = *(inbuffer++); /*length--;*/ count = *(inbuffer++); /*length--;*/ total += count +1; /* count of zero -> 1 byte */ while (count-- >= 0) { *(outbuffer++) = c; /*Putc(c, outf);*/ } } else { *(outbuffer++) = c; /*Putc(c, outf);*/ total++; } } if (total != 4096) fprintf(stderr, "internal error: bad undo_RLE\n"); #ifdef DEBUG printf("Exiting undo_RLE, length = %d (should be 0), total = %d (4096)\n", length, total); #endif } /* * Main entry point. * */ void unpak_SHK(srcfd, dstfd, comp_thread_eof, thread_eof, buffer) int srcfd, dstfd; fourbyt comp_thread_eof, thread_eof; onebyt *buffer; { twobyt CRC, blkCRC; onebyt vol; onebyt *wrbuf; /* points to buffer we're about to write */ int unlen, lzwflag, rleflag; unsigned int partial, toread, still_in_buf; fourbyt tmp4; /* temporary 4-byte variable */ static char *procName = "unpak_SHK"; CRC = 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 (read(srcfd, buffer, toread) < toread) Fatal("Bad read", procName); ibuf = buffer; /* set input pointer to start of buffer */ /* get header data */ blkCRC = Getc(inf); blkCRC += (Getc(inf) << 8); vol = (char) Getc(inf); /* disk volume #; not used here */ escape_char = (char) Getc(inf); /* RLE delimiter */ #ifdef DEBUG1 printf("vol = %d, escape_char = %x\n", vol, escape_char); #endif /* main loop */ while (thread_eof != (fourbyt) 0) { unlen = Getc(inf); unlen += (Getc(inf) << 8); #ifdef DEBUG1 printf("Length after compression = %d ($%.4x)\n", unlen, unlen); #endif lzwflag = Getc(inf); rleflag = (unlen != BLKSIZ); #ifdef DEBUG1 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)); } } #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. /* 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 + 5)) { /* 5 = 3 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(ibuf, buffer, still_in_buf, FALSE); 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_RLE reads from where you tell it and writes to rbuf */ if (lzwflag && rleflag) { undo_LZW(lbuf, unlen); /* from ibuf -> lbuf */ undo_RLE(lbuf, rbuf); /* from lbuf -> rbuf */ wrbuf = rbuf; /* write rbuf */ CRC = CalcCRC(CRC, rbuf, BLKSIZ); /* always 4K bytes */ } else if (lzwflag && !rleflag) { undo_LZW(lbuf, unlen); /* from ibuf -> lbuf */ wrbuf = lbuf; /* write lbuf */ CRC = CalcCRC(CRC, lbuf, BLKSIZ); } else if (!lzwflag && rleflag) { undo_RLE(ibuf, rbuf); /* from ibuf -> rbuf */ wrbuf = rbuf; /* write rbuf */ CRC = CalcCRC(CRC, rbuf, BLKSIZ); ibuf += unlen; /* have to skip over RLE-only data */ /* normally ibuf is advanced by Getc() calls */ } else { wrbuf = ibuf; /* write ibuf */ CRC = CalcCRC(CRC, ibuf, BLKSIZ); ibuf += partial; /* skip over uncompressed data */ /* normally ibuf is advanced by Getc() calls */ } #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, "\n"); else fprintf(stderr, "extract with V suboption to see filenames.\n"); } } !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive