[comp.binaries.apple2] NuLib v2.1.1 NuFX archiver

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