🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: mod.mac.sources
Article #42

Unpit version 0.3.1 Source (part 1 of 3)

#42
From: macintosh
Date: Fri, 13 Feb 1987 07:40
604 lines
21756 bytes
[Unpit version 0.3.1 Source - part 1 of 3]

---
/*

		unpit - Macintosh PackIt file packer/unpacker
		
   This program is a Macintosh translation/expansion of 'unpit', a
Unix program written by Allan G. Weber (Weber%Brand@USC-ECL) that
unpacks Packit-I and -II files.  Why port it to the Mac?  Well,
Packit-II is not free, but many people are posting {public domain,
noncommercial, or shareware} programs to USENET's net.sources.mac
and various other places in Packit-II format, apparently due to
the popularity achieved by the free Packit-I.  This program is
intended to get rid of the need to choose between these undesirable
alternatives:

    (1) losing access to freely-distributed software
        that's in Packit II format

    (2) uploading Packit-II files to a Unix box just
        for the purpose of running unpit on them (unpit
        is great if the files are already on the Unix
        box, but what does one do with files that come
        from, say, GENIE?)

    (3) forking over $10 not because one wants to use
        Packit-II but because one can't put up with (1)
        or (2), and has a sense of ethics that prevents:

    (4) using Packit-II to decode downloaded files without
        paying for it

Translation by Thomas D. Newton (ARPA address: Thomas.Newton@spice.cs.cmu.edu)

Copyright Information:
    By now, this program has a little bit of everything in it.  I
would assume that the Unix version is (C) 1986 Allan Weber, except
for the pieces taken from macput.c and macget.c.  I suspect that
this version doesn't have much of the macget/macput code left (when
on a Mac, it makes sense just to call the filesystem rather than to
write an .INFO file).  On the other hand, it does have some code
taken from BINConvert ((C) 1986 Thomas D. Newton), and some code
taken from SKEL, and some code from standard CS textbooks.  As I am
writing this using LightSpeed C, the executable version will have a 
C) Think notice.  Taking everything into account, this version of
Unpit is

        Copyright (C) 1986 Thomas D. Newton
        This program may be copied and used for noncommercial purposes.
            It may not be sold or offered as inducement to buy a product.
        Parts of this program are derived from code written by various
            authors which itself can be copied and used noncommercially.

Compilation Information:
    This program was compiled using LightSpeed C (tm) on a
    Macintosh.  It uses the "MacTraps" library, and the resources
    can be found in the companion file, 'unpit.proj.rsrc'.

Modification History:
    Version     Date     Who/Comments
    =======    ======    ============
      0.1      Aug 86    Thomas Newton (tdn@spice.cs.cmu.edu)
      			 Created.  Translated Unix 'unpit' to Macintosh;
      			 added SKEL-like user interface shell, Packit-I
      			 packing, delete file, and help commands; hacked
      			 up the structure of the program to gain speed.

      0.2      Aug 86    Thomas Newton (tdn@spice.cs.cmu.edu)
      			 Added code to call Disk Initialization Package
      			 so blank disks don't act "dead" when inserted.

      			 Removed CLOVER-B as an alias for EDIT'CLEAR --
      			 it doesn't seem to work with MockWrite, and it
      			 seems that keyboard events that happen while a
      			 DA window is active are not returned to Unpit,
      			 so Unpit can't "force" the alias to work.
      			 
      			 Fixed a few calls to Err() that didn't include
      			 the second parameter; changed main() to put up
      			 a dialog box rather than beeping if user tries
      			 to Print packed files from the Finder.

      0.3      Sep 86    Thomas Newton (tdn@spice.cs.cmu.edu)
      			 Added the ability to produce compressed packed
      			 files, using Huffman compression (Knuth's "The
      			 Art of Computer Programming", Volume One, Page
      			 402 describes how to build weighted trees with
      			 minimum total weight using Huffman's method).
      			 
      			 Reduced the amount of text in the "About" box,
      			 both to get the size of the executable version
      			 down and because Unpit is now roughly equal to
      			 PackIt-II, rather than to PackIt-1.5 (not that
				 there ever was a Packit-1.5 . . .).

      0.3.1		Dec 86    Reid Ellis (unicus!rae@yetti.uucp)
      			 Added the ability to do "batch" packing by selecting
      			 all the files to be packed from the finder and
      			 holding the option key down whilst booting.
      			 Moved all resources to a '.rsrc' file (I use
      			 ResEdit 1.0 to do resource munging).
-------------------------------------------------------------------------------

Format of a Packit file:

Repeat the following sequence for each file in the Packit file:

	4 byte identifier ("PMag" = not compressed, "Pma4" = compressed)
	variable length compression data (if compressed file)
	92 byte header (see struct pit_header below) *
	2 bytes CRC number *
	data fork (length from header) *
	resource fork (length from header) *
	2 bytes CRC number *

Last file is followed by the 4 byte Ascii string, "Pend", and then the EOF.

* these are in compressed form if compression is on for the file

*/

#include <DialogMgr.h>
#include <EventMgr.h>
#include <FileMgr.h>
#include <MacTypes.h>
#include <MemoryMgr.h>
#include <MenuMgr.h>
#include <pascal.h>
#include <Quickdraw.h>
#include <SegmentLdr.h>
#include <StdFilePkg.h>
#include <WindowMgr.h>

typedef char byte;
typedef int  word;

struct pit_header {	/* Packit file header (92 bytes)
	byte nlen;	/* number of characters in packed file name */
	byte name[63];	/* name of packed file */
	byte type[4];	/* file type */
	byte auth[4];	/* file creator */
	word flags;	/* Finder flags */
	word lock;	/* low-order bit seems to be lock; others are ??? */
	long dlen;	/* number of bytes in data fork */
	long rlen;	/* number of bytes in resource fork */
	long ctim;	/* file creation time */
	long mtim;	/* file modified time */
};

#define HDRBYTES  92

#define BYTEMASK 0xff

#define H_NAMELEN 63

#define H_NLENOFF 0
#define H_NAMEOFF 1
#define H_TYPEOFF 64
#define H_AUTHOFF 68
#define H_FLAGOFF 72
#define H_LOCKOFF 74
#define H_DLENOFF 76
#define H_RLENOFF 80
#define H_CTIMOFF 84
#define H_MTIMOFF 88

#define INITED 0x0100
#define FLOCK  0x01

/* Screen locations of SFGetFile and SFPutFile boxes */
#define GET_FILE_X  82
#define GET_FILE_Y  92
#define PUT_FILE_X 104
#define PUT_FILE_Y  92

/* Screen location of disk initialization dialog */
#define INIT_X     112
#define INIT_Y      80


typedef struct nod {
	int flag, byte, index2;
	long count;
	struct nod *one, *zero;
} node, *nodeptr;

node    nodelist[512];			/* 512 should be big enough */
nodeptr heap[513], freenode;

struct {
	long bits;
	int bcount;
} byte_code[256];

unsigned char hdr[HDRBYTES];

typedef int crctype;

long datalen, rsrclen;
int decode, bit;

Boolean Filter;		/* if TRUE, Unpack... shows only PIT and TEXT files */
Boolean Compress;       /* if TRUE, Pack...   uses Huffman compression      */
Boolean optkeyDown;

/* forward declarations */
nodeptr		read_tree();
long		get4();
crctype		read_hdr();
crctype		write_fork();
crctype		getcrc();
Boolean		pack_files();

/*
 * Error codes that can be used as the second parameter to Err (a function
 * declared below).  They let Err() know roughly what the program was doing
 * at the time that an error occurred so that it can display somewhat more
 * informative error messages.
 */
 
enum {
    errCreateFile,	/* creating output file */
    errOpenDataFk,	/* opening data fork for write */
    errOpenRsrcFk,	/* opening resource fork for write */
    errGetFInfo,	/* getting information for unpacked file */
    errSetFInfo,	/* setting information for unpacked file */
    errWriting,		/* writing output file */
    errReading,		/* reading input file */
    errLock,		/* locking unpacked file */
    errDelete		/* deleting file */
    };
    
/*
Report an error to the user.  This function is used to process the return
codes received from FSOpen and the like.
############################       Err     ##############################
*/
Boolean Err(theErr, activity)
OSErr theErr;
int activity;
{   char *errText, *errWhy;

    if (theErr == noErr)
        return FALSE;
    else {
        switch (activity) {
            case errCreateFile:
            	errWhy = "\Pcreating output file"; break;
            case errOpenDataFk:
            	errWhy = "\Popening data fork"; break;
            case errOpenRsrcFk:
            	errWhy = "\Popening resource fork"; break;
            case errGetFInfo:
            	errWhy = "\Pgetting Finder information"; break;
            case errSetFInfo:
            	errWhy = "\Psetting Finder information"; break;
            case errWriting:
            	errWhy = "\Pwriting to output file"; break;
            case errReading:
                errWhy = "\Preading from Packit file"; break;
            case errLock:
                errWhy = "\Plocking unpacked file"; break;
            case errDelete:
                errWhy = "\Ptrying to delete file"; break;
            default:
                errWhy = "\P<doing random activity>"; break;
            }
            
        switch (theErr) {
	    case bdNamErr:	errText = "\PBad file name"; break;
            case dupFNErr:	errText = "\PDuplicate file name"; break;
	    case dirFulErr:	errText = "\PDirectory full"; break;
	    case extFSErr:	errText = "\PExternal file system"; break;
	    case ioErr:		errText = "\PDisk I/O error"; break;
	    case nsvErr:	errText = "\PNo such volume"; break;
	    case vLckdErr:	errText = "\PSoftware volume lock"; break;
	    case wPrErr:	errText = "\PHardware volume lock"; break;
	    case mFulErr:	errText = "\PMemory full"; break;
	    case tmfoErr:	errText = "\PToo many files open"; break;
	    case dskFulErr:	errText = "\PDisk full"; break;
	    case fLckdErr:	errText = "\PFile locked"; break;
	    case fnOpnErr:	errText = "\PFile not open"; break;
	    case opWrErr:	errText = "\PFile already open for writing"; break;
	    case wrPermErr:	errText = "\PPermission doesn't allow writing"; break;
            default:            errText = "\P<unanticipated error>"; break;
	    }
	    
#define alertboxid 258
	ParamText(errWhy,errText,0L,0L);
	StopAlert(alertboxid, 0L);
        return TRUE;
        }
}

/*
 * Error codes that can be used as parameters to Err2 (below)
 */
 
enum {
    errHeaderCRC,	/* file header CRC mismatch */
    errFileCRC,		/* file data/rsrc CRC mismatch */
    errNotPackit,	/* unrecognized PACKIT format */
    errBadPrint,        /* attempt to Print files from Finder */
    errCompress         /* internal error: out of room for compression */
    }

/*
Report an error to the user.  This function is used for error messages
that aren't directly caused by I/O errors
############################      Err2     ##############################
*/

Err2(theErr)
int theErr;
{
    switch (theErr) {
        case errHeaderCRC:
            ParamText("\PError: file header CRC mismatch",0L,0L,0L);
            break;
        case errFileCRC:
    	    ParamText("\PError: file data/resource CRC mismatch",0L,0L,0L);
    	    break;
    	case errNotPackit:
    	    ParamText("\PThis is not a Packit-I or Packit-II file.",0L,0L,0L);
    	    break;
    	case errBadPrint:
    	    ParamText("\PPrinting packed files is not supported.",0L,0L,0L);
    	    break;
    	case errCompress:
    	    ParamText("\POut of bits for character codes.",0L,0L,0L);
    	    break;
    }
    StopAlert(alertboxid+1,0L);
}

/*
Open a file's resource fork
##############################   Open_Rsrc   ##############################
*/
Open_Rsrc(fName, vRefNum, perm, ref_num)
StringPtr fName;
int       vRefNum, perm;
int       *ref_num;
{   ParamBlockRec ParamB;
    int ret_val;

    ParamB.ioParam.ioCompletion = 0L;
    ParamB.ioParam.ioNamePtr = fName;
    ParamB.ioParam.ioVRefNum = vRefNum;
    ParamB.ioParam.ioVersNum = 0;
    ParamB.ioParam.ioPermssn = perm;
    ParamB.ioParam.ioMisc = 0L;
    ret_val = PBOpenRF(&ParamB,0);
    *ref_num = ParamB.ioParam.ioRefNum;
    return ret_val;
}

/*
Compare two strings that are four bytes long.  This allows us to toss out
the Strings library, since Unpit was pulling it in just for strncmp . . .
##############################   Str_Cmp_4   ##############################
*/
Boolean str_cmp_4(str1, str2)
char *str1, *str2;
{   int i;
    for (i = 0; i < 4; i++)
        if (str1[i] != str2[i]) return FALSE;
    return TRUE;
}

/*
Read a byte from the file denoted by pit_ref_num, using buffered reads.
Note: this routine is bypassed for speed when unpacking Packit-I files.
##############################   Read_Byte   ##############################
*/
#define BSIZE 4096
int pit_ref_num;			/* Mac filesystem reference number */
long pit_bytes_left;			/* # of bytes to be read from disk */
unsigned char pit_buffer[BSIZE];	/* Buffer for bytes read from disk */
int pit_index, pit_max_index;		/* For tracking the buffer's state */

#define read_byte(ARG1) \
	((pit_index < pit_max_index) ? pit_buffer[pit_index++] : do_read(ARG1))

do_read(eflag)
Boolean *eflag;
{   long Count;
    unsigned char Ch;

    if (pit_index < pit_max_index)
        return pit_buffer[pit_index++];
    else
        if (pit_bytes_left == 0L) {
            Err(ioErr,errReading);
            *eflag = TRUE;
            return 0;
            }
        else {
	    Count = (pit_bytes_left < BSIZE) ? pit_bytes_left : BSIZE;
	    pit_index = 0; pit_max_index = (int) Count; pit_bytes_left -= Count;
	    if (Err(FSRead(pit_ref_num, &Count, &pit_buffer), errReading)) {
	        *eflag = TRUE;
	        return 0;
		}
	    else
	        return pit_buffer[pit_index++];
	}
}

/*
Update the value of CRC to include the bytes in a random chunk of memory.
###############################   Upd_Crc   ###############################
*/
crctype upd_crc(crc, theBytes, byteCount)
register crctype crc;
unsigned char   *theBytes;
long             byteCount;
{	register int  i;
	register long n;
	
	for (n = 0; n < byteCount; n++) {
		crc = crc ^ ((int)theBytes[n] << 8);
		for (i = 0; i < 8; i++)
			if (crc & 0x8000)
				crc = (crc << 1) ^ 0x1021;
			else
				crc <<= 1;
	}
	return(crc & 0xffff);
}

/*
Put up a dialog box that allows the user to select a file to unpack.
################################   UnPack   ###############################
*/
unpack()
{   SFReply PitRecord;
    SFTypeList PitTypes;
    Point where;
    ParamBlockRec ParamB;
    
    where.h = GET_FILE_X; where.v = GET_FILE_Y;
    PitTypes[0] = 'PIT '; PitTypes[1] = 'TEXT';
    SFGetFile(where, "", 0L, Filter ? 2 : -1, PitTypes, 0L, &PitRecord);
    if (PitRecord.good) {
        ParamB.fileParam.ioCompletion = 0L;
        ParamB.fileParam.ioNamePtr = PitRecord.fName;
        ParamB.fileParam.ioVRefNum = PitRecord.vRefNum;
        ParamB.fileParam.ioFVersNum = 0;
        ParamB.fileParam.ioFDirIndex = 0;	/* use NamePtr/VRefNum */
        PBGetFInfo(&ParamB,0);
        pit_bytes_left = ParamB.fileParam.ioFlLgLen;
        if (pit_bytes_left < 4L)
            Err2(errNotPackit);   /* more meaningful than "I/O Error..." */
        else {
            pit_index = 0; pit_max_index = -1;
            FSOpen(PitRecord.fName,PitRecord.vRefNum,&pit_ref_num);
            unpit();
            FSClose(pit_ref_num);
        }
    }
}

/*
Put up a dialog box that allows the user to delete a file.
################################   Delete   ###############################
*/
#define deleteconfirm 260
delete()
{   SFReply DelRecord;
    Point where;

    where.h = GET_FILE_X; where.v = GET_FILE_Y;
    SFGetFile(where, "", 0L, -1, 0L, 0L, &DelRecord);
    if (DelRecord.good) {
        ParamText(DelRecord.fName, 0L, 0L, 0L);
        if (CautionAlert(deleteconfirm, 0L) == 1)	/* the OK button */
            Err(FSDelete(DelRecord.fName, DelRecord.vRefNum),errDelete);
    }
}


/*
Unpack a Packit-I or Packit-II files into its component files.
################################   UnPit   #################################

    This routine assumes that the input file has already been opened and
    that the input file can be read one byte at a time using read_byte().
    Errors that occur during the attempted unpacking will (hopefully) be
    reported to the user within UnPit or functions it calls; in any case,
    the caller should close the input file when UnPit returns.

*/
#define statusid 263
unpit()
{   char temp[4];		/* used when searching for Packit identifier */
    SFReply OutRecord;		/* names and volume numbers of output files  */
    Point where;		/* where to locate the SFPutFile dialog box  */
    Boolean saveOutput;		/* to allow extracting pieces of a .PIT file */
    ParamBlockRec ParamB;	/* needed to make low-level filesystem calls */
    int out_ref_num, i;
    crctype data_crc, crc;
    Boolean eflag = FALSE;
    DialogPtr theDialog = 0L;

    while(1) {
        for (i = 0; i < 4; i++) {		/* PACKIT signature */
            temp[i] = read_byte(&eflag);
            if (eflag) return;
            }
	if (str_cmp_4(temp, "PMag") || str_cmp_4(temp, "PMa4")) {
	    if (temp[3] == '4') {
		freenode = nodelist;
		read_tree(&eflag); if (eflag) return;
		decode = 1;
		}
	    else
	        decode = 0;
	    data_crc = read_hdr(&eflag); if (eflag) return;
	    crc = getcrc(&eflag); if (eflag) return;
	    if (crc != data_crc) {
                Err2(errHeaderCRC);
	        return;
	    }
	    
	    /* Get name of output file */
	    where.h = PUT_FILE_X; where.v = PUT_FILE_Y;
	    SFPutFile(where, "\PSave as:", &hdr[H_NLENOFF], 0L, &OutRecord);
	    if (OutRecord.good) {
	        if ((i = Create(OutRecord.fName,OutRecord.vRefNum,'????','????'))
	            == dupFNErr) { /* user said it was OK to replace...delete! */
	            FSDelete(OutRecord.fName, OutRecord.vRefNum);
	            i = Create(OutRecord.fName,OutRecord.vRefNum,'????','????');
	        }
	        if (Err(i,errCreateFile)) return;
	        saveOutput = TRUE;
	        if (Err(FSOpen(OutRecord.fName,OutRecord.vRefNum,&out_ref_num),
			errOpenDataFk)) return;
	        }
	    else {
	        saveOutput = FALSE;
	        out_ref_num = 0;
	    }
	    
	    /* Give visual indication that file is being unpacked */
	    theDialog = GetNewDialog(statusid, 0L, -1L);
	    ParamText(0L,0L,saveOutput ? "\PUnpack":"\PSkipp",OutRecord.fName);
	    DrawDialog(theDialog);

	    /* First, take care of writing the file's data fork */
	    data_crc = write_fork(saveOutput, out_ref_num, datalen,
	    			  (crctype) 0, &eflag);
	    if (saveOutput) FSClose(out_ref_num);
	    if (eflag) goto error;
	    
	    /* Next, take care of writing the file's resource fork. */
	    if (saveOutput && (rsrclen > 0)) {
	        if (Err(Open_Rsrc(OutRecord.fName,OutRecord.vRefNum,fsWrPerm,
	                &out_ref_num), errOpenRsrcFk)) goto error;
	        }
	    else
	        out_ref_num = 0;
	    data_crc = write_fork(saveOutput, out_ref_num, rsrclen,
	    			  data_crc, &eflag);
	    if (saveOutput) FSClose(out_ref_num);
	    if (eflag) goto error;

	    /* Check the CRC to make sure the file was unpacked intact */
	    crc = getcrc(&eflag);
	    if (eflag || (crc != data_crc)) {
		Err2(errFileCRC);
		if (saveOutput) FSDelete(OutRecord.fName, OutRecord.vRefNum);
		goto error;
	    }

	    /* Finally, set the filesystem and Finder info for the file */
	    if (saveOutput) {
		ParamB.fileParam.ioCompletion = 0L;
		ParamB.fileParam.ioNamePtr = OutRecord.fName;
		ParamB.fileParam.ioVRefNum = OutRecord.vRefNum;
		ParamB.fileParam.ioFVersNum = 0;
		ParamB.fileParam.ioFDirIndex = 0;    /* use NamePtr/VRefNum */
		if (Err(PBGetFInfo(&ParamB,0),errGetFInfo)) goto error;

		ParamB.fileParam.ioFlFndrInfo.fdType = get4(&hdr[H_TYPEOFF]);
		ParamB.fileParam.ioFlFndrInfo.fdCreator = get4(&hdr[H_AUTHOFF]);
		ParamB.fileParam.ioFlFndrInfo.fdFlags =
			(hdr[H_FLAGOFF] << 8) | hdr[H_FLAGOFF+1];
		ParamB.fileParam.ioFlCrDat = get4(&hdr[H_CTIMOFF]);
		ParamB.fileParam.ioFlMdDat = get4(&hdr[H_MTIMOFF]);

		/* turn INITed flag off since Finder hasn't seen file yet */
		ParamB.fileParam.ioFlFndrInfo.fdFlags &= (0xFFFF ^ INITED);
		if (Err(PBSetFInfo(&ParamB,0),errSetFInfo)) goto error;

		/* lock file if Packit header says to do so */
		if (hdr[H_LOCKOFF+1] & 1)    /* don't look at other bits */
		    if (Err(SetFLock(OutRecord.fName,OutRecord.vRefNum),errLock))
		        goto error;

		/* finally, flush volume to make sure that data is safe */
		FlushVol(0L,OutRecord.vRefNum);
--- end of part 1 ---
Technical Details
Message-ID: <2277@felix.UUCP>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!water!watnot!watmath!clyde!cuae2!ihnp4!ptsfa!lll-lcc!pyramid!hplabs!felix!macintosh

Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: arc1-ca.pugleaf.net!utzoo!utgpu!water!watnot!watmath!clyde!cuae2!ihnp4!ptsfa!lll-lcc!pyramid!hplabs!felix!macintosh
From: macintosh@felix.UUCP
Newsgroups: mod.mac.sources
Subject: Unpit version 0.3.1 Source (part 1 of 3)
Message-ID: <2277@felix.UUCP>
Date: Fri, 13-Feb-87 02:40:47 EST
Article-I.D.: felix.2277
Posted: Fri Feb 13 02:40:47 1987
Date-Received: Sat, 14-Feb-87 15:04:44 EST
Sender: macintosh@felix.UUCP
Reply-To: macintosh@felix.UUCP (The Moderator)
Organization: FileNet Corp., Costa Mesa, Ca.
Lines: 604
Approved: bytebug@felix.UUCP (Roger L. Long)