karl@sugar.hackercorp.com (Karl Lehenbauer) (10/07/89)
:
#! /bin/sh
# This is a shell archive,
# created by Karl Lehenbauer on Fri Oct 6 12:34:41 1989
# Remove anything before the "#! /bin/sh" line, then unpack it by saving
# it into a file and typing "sh file". If you do not have sh, you need
# unshar, a dearchiving program which is widely available. In the absolute
# wost case, you can crack the files out by hand.
# If the archive is complete, you will see the message "End of archive."
# at the end.
# This archive contains the following files...
# 'README'
# 'Makefile'
# 'iff.h'
# 'assert.h'
# 'cleanup.c'
# 'create.c'
# 'delete.c'
# 'extract.c'
# 'iff.c'
# 'includes.c'
# 'main.c'
#
# To extract them, run the following through /bin/sh
echo x - README
sed 's/^X//' > README << '//END_OF_FILE'
Xiffar - IFF CAT Archiver, Release 1.4, 9/23/89
X
XWritten by:
X
X Karl Lehenbauer/Hackercorp
X 3918 Panorama
X Missouri City, TX 77459
X
X Usenet: ..uunet!sugar!karl
X Internet: karl@sugar.hackercorp.com
X BIX: kelehen
X
XAll liability is disclaimed! This code is free. We do not have a contract.
X
XThis software, source and binary, is released to the public domain, 5/9/88.
X
XI ask that you retain my name as the original author in the source and
Xdocumentation if you redistribute this and that I be credited in the user
Xmanual if iffar is redistributed with a commercial product.
X
XSubsequent updates are also released to the public domain.
X
XRegards, Karl Lehenbauer @ The Hacker's Haven, Missouri City, Texas, 7/31/89
X
X
X---------------------------------------------------------------------
X
XNAME
X
X iffar - IFF CAT archiver
X
XSYNOPSIS
X
X iffar option [posname] archive_file [IFF_file] ...
X
XDESCRIPTION
X
X Iffar maintains archives of Interchange File Format (IFF) FORM and CAT
X files in a manner that complies with the IFF CAT specification.
X
X IFF CAT archives should be portable to different machines. No promises.
X
X The option string must start with 'd', 'q', 'r', 't' or 'x' and may
X additionally have modifiers of 'a', 'b', 'c', 'i' or 'v'. Not all
X modifiers are valid with all options.
X
X The options are:
X
X 'd' Delete named IFF files from the CAT archive.
X
X 'q' Quickly append named IFF files to the CAT archive.
X
X 'r' Replace named IFF files in the CAT archive; append new ones.
X
X 't' Print a table of contents of the CAT archive.
X
X 'x' Extract named IFF files from the CAT archive.
X If no names are specified, all files are extracted.
X
X
X The modifiers are:
X 'a' "after", replace or append IFF files after entry in CAT
X archive named by posname
X
X 'b' "before", replace or append IFF files before entry in CAT
X archive named by posname
X
X 'c' Do not print a message indicating archive is being created
X when it must be created.
X
X 'i' a synonym for "before"
X
X 'v' print verbose description of all activity
X For table of contents, prints IDs, and
X lengths of chunks within the IFF file chunks
X in the CAT archive. It prints the contents of
X chunks that it knows to be textual and short.
X
X
X On all operations that modify the archive, except for "quick append",
X the archive will be renamed with a ".old" extension and a new archive
X will be created.
X
X The "quick append" option causes the named files to be added to the
X end of the archive without rewriting the archive or looking to see
X if the entry already exists. This is to avoid quadratic behavior
X when building up an archive one or just a few entries at a time.
X
X Wildcards of the semi-Unix-type as supplied by Manx are provided.
X
X Only the basename of the specified IFF filenames will be used for
X element names within the archive. In other words, pathnames are
X stripped from filenames to create the archive element name.
X This is also true for extracts.
X
XEXAMPLES
X
X iffar x foo ram:t/bar
X
X would extract element "bar" from archive "foo" into file
X "ram:t/bar"
X
X iffar ra sounds Cabasa dh0:sounds/Snare
X
X would replace a FORM, CAT or LIST named Snare in the
X archive with dh0:sounds/Snare, placing Snare directly
X after Cabasa. If Cabasa is not found in the archive,
X Snare is placed at the end.
X
XBUGS
X
X The archive will be corrupted if a write error (including
X running out of disk space) occurs during "quick
X append" mode and there won't be a ".old" backup file left
X either.
X
X Running out of disk space leaves corrupted archives.
X For all options but "q", the ".old" backup file will contain
X the archive in its state prior to the run that blew it up.
X The program should delete the corrupted archive and restore
X the ".old" file, but it doesn't.
X
X A user contacted me after grabbing the 1.2 version and insisted
X that it is illegal under the IFF spec to add chunks to FORMs that
X your program doesn't understand. If you agree with this, iffar
X must be considered broken because it adds a chunk that it
X uses to identify the embedded FORM or CAT. iffar strips this
X chunk when extracting a file, however, but there were conflicts
X when FORMs already contained FNAM chunks.
X I have given a lot of thought to this and I believe from a
X careful reading of the spec that it is legal to add chunks to FORMs
X your program doesn't understand. I have renamed iffar's chunk ID
X from FNAM to IFAR which should vastly reduce the possibility of a
X conflict.
X I would like to rewrite iffar to not embed the ID chunk within
X the form but rather to keep it outside the form, though still in
X an IFF manner, which would solve the problem. This would reduce the
X complexity of the program and increase its performance. However,
X it's a lot of work and the archiver is useful (to me at least) as
X is, hence the 1.3 version is a spruce-up rather than a rewrite.
X
X iffar doesn't archive IFF LIST files properly. They're rare.
X
X
XIMPLEMENTATION NOTES
X
X Iffar is written to run under Manx Aztec C 3.6a for the Amiga
X under AmigaDOS version 1.3.
X
X My IFF archiving routines were written to be well-behaved from an
X IFF point of view; that is, they try to respect the virtual
X end-of-file defined by the size field of a chunk's header when
X diving through the chunk's subchunks. It makes for more work to use
X the routines, but adds some certainty that the program is working
X properly. I don't know. The IFF spec requests that we do, so I do.
X
X Note that when converting your code to use a CAT file instead of
X reading several FORM files (it's about twice as fast for an
X application of mine involving about 25 files from two to ten
X kilobytes each, reading them in from floppy.), your code needs
X to either be driven by the files read from the CAT (you search
X a list of names you're looking for when you see a FORM and load
X what you want) or your code has to know what's coming from the
X CAT specifically. I'd prefer that you do the former, of course.
X The table of contents routines (toc.c) provide a reasonable template
X for your CAT-reading application.
X
XNOTES FOR THOSE WISHING TO PORT THE ARCHIVER
X
X Knock yourselves out. The code is written using standard C library
X calls, with the exception of calls to "scdir", which Manx uses as
X a means of expanding wildcards. If you get it to work on a different
X system, such as Unix System V, please send me a copy, preferably
X with #ifdefs so the Manx stuff still works. Ultimately, I think an
X alternative "_main" startup routine should be written for the Amiga
X that expands wildcards inline to provide an argc and argv as they
X would look on Unix after the shell had expanded all the wildcards.
X
X
XREVISION HISTORY
X
X4/25/88, version 1.1
X
X Initial version - never distributed, as far as I know
X
X5/9/88, version 1.2
X
X Made 'r' (replace) option create the archive if it isn't there.
X
X Fixed bug that caused the IFF reader to get lost when doing
X a verbose table of contents on certain archives.
X
X Flattened the source directory structure.
X
X Updated the documentation.
X
X7/31/89, version 1.3
X
X Changed the chunk type iffar uses as the name for embedded FORMs and
X CATs from FNAM to IFAR to reduce conflict with certain FORMs.
X
X Updated the documentation.
X
X Compiled and tested under AmigaDOS 1.3.
X
X
X9/23/89, version 1.4
X
X Fixed bug where some archive entry names would be truncated
X
X
X-----------------------------------------------------------------------------
//END_OF_FILE
echo x - Makefile
sed 's/^X//' > Makefile << '//END_OF_FILE'
X# iffar - IFF CAT archiver, makefile
X#
X# By Karl Lehenbauer, version 1.3, release date 7/31/89.
X# This code is released to the public domain.
X# See the README file for more information.
X#
X# This makefile is for Manx Aztec C 3.6a for the Commodore Amiga computer.
X# It probably works under 3.4, but I haven't tried it.
X#
X
XCFLAGS= +p +Iincludes.pre
X#CFLAGS= -n +p -DDEBUG +Iincludes.pre
X
XSDBFLAGS=
X#SDBFLAGS= -g
X
XHFILES= assert.h iff.h
X
XOFILES= main.o cleanup.o create.o toc.o iff.o \
X extract.o delete.o replace.o quickappend.o misc.o
X
X.c.o:
X cc $(CFLAGS) $*.c
X
Xall: iffar
X say "ready"
X
Xclean:
X -delete #?.o quiet
X -delete #?.bak quiet
X -delete includes.pre quiet
X
Xscratch: clean all
X
Xiffar: includes.pre $(OFILES)
X ln $(SDBFLAGS) +q -o iffar $(OFILES) -lcl32
X
Xincludes.pre: $(HFILES) includes.c
X cc +p +Hincludes.pre includes.c
X
X
//END_OF_FILE
echo x - iff.h
sed 's/^X//' > iff.h << '//END_OF_FILE'
X#ifndef IFF_H
X#define IFF_H
X/*
X IFF.H defs for IFF-85 Interchange Format Files. 1/22/86
X
X By Jerry Morrison and Steve Shaw, Electronic Arts.
X
X Mods and stripping of a lot of stuff by Karl Lehenbauer, 1987, 1988
X
X This software is in the public domain.
X*/
X
X#ifndef LIBRARIES_DOS_H
X#include "libraries/dos.h"
X#endif
X
Xtypedef LONG ID;
X
X/* Four-character IDentifier builder.*/
X#define MakeID(a,b,c,d) ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) )
X
X/* Standard group IDs. A chunk with one of these IDs contains a
X SubTypeID followed by zero or more chunks.*/
X#define ID_FORM MakeID('F','O','R','M')
X#define ID_PROP MakeID('P','R','O','P')
X#define ID_LIST MakeID('L','I','S','T')
X#define ID_CAT MakeID('C','A','T',' ')
X#define ID_FILLER MakeID(' ',' ',' ',' ')
X#define ID_IFAR MakeID('I','F','A','R')
X#define ID_MISC MakeID('M','I','S','C')
X
X/* The IDs "FOR1".."FOR9", "LIS1".."LIS9", & "CAT1".."CAT9" are reserved
X * for future standardization.*/
X
X/* ---------- Chunk ----------------------------------------------------*/
X
X/* All chunks start with a type ID and a count of the data bytes that
X follow--the chunk's "logicl size" or "data size". If that number is odd,
X a 0 pad byte is written, too. */
X
Xtypedef struct {
X ID ckID;
X LONG ckSize;
X } ChunkHeader;
X
Xtypedef struct {
X ID ckID;
X LONG ckSize;
X UBYTE ckData[ 1 /*REALLY: ckSize*/ ];
X } Chunk;
X
X/* define the maximum reasonable chunk size - the real max is around 2^32,
X * but we need some reasonability limit to check for */
X#define MAXCHUNKSIZE 900000
X
X/* The Grouping chunks (LIST, FORM, PROP, & CAT) contain concatenations of
X * chunks after a subtype ID that identifies the content chunks.
X * "FORM type XXXX", "LIST of FORM type XXXX", "PROPerties associated
X * with FORM type XXXX", or "conCATenation of XXXX".*/
Xtypedef struct {
X ID ckID;
X LONG ckSize; /* this ckSize includes "grpSubID".*/
X ID grpSubID;
X } GroupHeader;
X
Xtypedef struct {
X ID ckID;
X LONG ckSize;
X ID grpSubID;
X UBYTE grpData[ 1 /*REALLY: ckSize-sizeof(grpSubID)*/ ];
X } GroupChunk;
X
X
X#endif IFF_H
X
//END_OF_FILE
echo x - assert.h
sed 's/^X//' > assert.h << '//END_OF_FILE'
X/* hackercorp - hackercore assert file for Amiga
X * kel 12/87 v1.0
X */
X
X#ifndef NODEBUG
X#ifndef stderr
X#include <stdio.h>
X#endif
X#define assert(x) if (!(x)) {fprintf(stderr,"Assertion failed: x, file %s, line %d\n",__FILE__,__LINE__); cleanup(); exit(1);}
X#else
X#define assert(x)
X#endif
//END_OF_FILE
echo x - cleanup.c
sed 's/^X//' > cleanup.c << '//END_OF_FILE'
X/* cleanup - Hackercorp Hackercore, standard cleanup management routines, v1.2
X
X This code is released to the public domain, 5/9/88, by Peter da Silva
X and Karl Lehenbauer
X
X Usage:
X
X add_cleanup(cleanup_routine);
X void cleanup_routine();
X
X Add a routine to the cleanup list. When cleanup is called, routines
X passed as arguments to add_cleanup will be executed in the reverse
X order in which they were received. See the source to the IFF CAT
X archiver, iffar, for an example of how to use this.
X
X void cleanup()
X
X Execute all the routines passed as arguments to add_cleanup
X
X panic(s)
X char *s;
X
X Abort the program by printing a panic message including string "s"
X on stderr, flushing stdout and stderr, calling cleanup and exiting.
X
X _abort()
X By defining _abort to call panic, entering control-C while using
X stdio will cause the program to abort, executing your cleanup
X routines. Also note that Manx sdb calls _abort when the user
X requests an exit, so your cleanup routines will be executed then
X as well.
X*/
X
X#include <exec/memory.h>
X#include <stdio.h>
X
Xstruct _clean
X{
X int (*function)();
X struct _clean *next;
X} *cleanlist = NULL;
X
X/* add_cleanup
X * given a function, add it to a list of functions to call when cleanup
X * is executed
X */
Xadd_cleanup(function)
Xint (*function)();
X{
X struct _clean *ptr;
X
X ptr = AllocMem(sizeof(struct _clean), MEMF_PUBLIC);
X if(!ptr)
X return 0;
X ptr->function = function;
X ptr->next = cleanlist;
X cleanlist = ptr;
X}
X
X/* cleanup
X * call all the functions that were passed as arguments to add_cleanup
X * this run
X */
Xcleanup()
X{
X struct _clean *ptr;
X int (*f)();
X
X while(cleanlist)
X {
X /* locate the next cleanup function and get the function pointer */
X ptr = cleanlist;
X cleanlist = cleanlist->next;
X f = ptr->function;
X
X /* cleanup must clean up after itself */
X FreeMem(ptr, sizeof(struct _clean));
X
X /* execute the function */
X (*f)();
X }
X}
X
X/* panic - abort with an error message */
X
Xshort panic_in_progress = 0;
X
Xpanic(s)
Xchar *s;
X{
X fflush(stdout);
X fprintf(stderr,"panic: %s\n",s);
X fflush(stderr);
X if (!panic_in_progress)
X {
X cleanup();
X exit(10);
X }
X fprintf(stderr,"double panic!\n");
X exit(11);
X}
X
X_abort()
X{
X panic("^C or other C library abort");
X}
//END_OF_FILE
echo x - create.c
sed 's/^X//' > create.c << '//END_OF_FILE'
X/* iffar - IFF CAT archiver output and file copy rouines
X
X By Karl Lehenbauer, version 1.2, release date 5/9/88.
X This code is released to the public domain.
X See the README file for more information.
X
X*/
X
X/* culled from general purpose IFF file cracking routines for Karl's
X * Audio Stuff by Karl Lehenbauer, based originally on public domain IFF
X * code from Electronic Arts, 2/24/88
X */
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include "assert.h"
X
X#include "iff.h"
X
Xextern long lseek();
X
Xextern int verbose;
X
Xextern char *basename();
X
X#define ID_MISC MakeID('M','I','S','C')
X
XWriteCATheader(fd)
Xint fd;
X{
X static ULONG dummy = ID_MISC;
X WriteChunk(fd,ID_CAT, &dummy, sizeof(dummy));
X}
X
X/* write a chunk header, that's the 4-byte chunk ID and a 4-byte
X * chunk length
X */
XWriteChunkHeader(fd,chunktype,length)
Xint fd;
XULONG chunktype;
Xlong length;
X{
X ChunkHeader chunkheader;
X
X chunkheader.ckID = chunktype;
X chunkheader.ckSize = length;
X
X if (write(fd,&chunkheader,sizeof(chunkheader)) == -1)
X {
X perror("WriteChunkHeader");
X return(0);
X }
X return(1);
X}
X
X/* write a chunk that has a four character subtype, like FORM, CAT or LIST
X*/
XWriteSuperChunkHeader(fd,chunktype,subtype,length)
Xint fd;
XULONG chunktype, subtype;
Xlong length;
X{
X if (!WriteChunkHeader(fd,chunktype,length+sizeof(subtype)))
X return(0);
X return(write(fd,&subtype,sizeof(subtype)) != -1);
X}
X
X/* WriteCATentry
X This routine is given all of the info for a superchunk header and,
X in addition, a file name. It writes the chunk header and an
X IFAR chunk containing the file name out to the fd provided.
X*/
XWriteCATentry(fd,fname,chunktype,subtype,length)
Xint fd;
Xchar *fname;
XULONG chunktype, subtype;
Xlong length;
X{
X int fnamelen;
X int calc_chunk_length;
X
X /* Figure out the length of the file name. Remember that
X * it should be even. (I should use a cool macro to force
X * that, but I don't)
X * Add the size of the IFAR chunk we're going to write to our
X * calculated chunk length.
X */
X fnamelen = strlen(fname);
X calc_chunk_length = fnamelen;
X if (fnamelen & 1)
X calc_chunk_length++;
X calc_chunk_length += length + sizeof(ChunkHeader);
X
X WriteSuperChunkHeader(fd,chunktype,subtype,calc_chunk_length);
X if (!WriteChunk(fd,ID_IFAR,fname,strlen(fname)))
X return(0);
X return(1);
X}
X
X/* write a chunk header and body, that's the 8-byte header and the data
X * to match the specified length
X */
XWriteChunk(fd,chunktype,data,length)
Xint fd;
XULONG chunktype;
Xchar *data;
Xlong length;
X{
X static char zero = '\0';
X
X if (!WriteChunkHeader(fd,chunktype,length))
X return(0);
X
X /* if there's a body, write it out */
X if (length != 0)
X if (write(fd,data,length) == -1)
X {
X perror("WriteChunk");
X return(0);
X }
X
X /* if the length is odd, write out an additional zero byte */
X if (length & 1)
X if (write(fd,&zero,1) == -1)
X {
X perror("WriteChunk");
X return(0);
X }
X return(1);
X}
X
X/* checknew - given fname, this routine prints "Creating IFF CAT archive "
X * and the fname if file fname does not exist
X */
Xchecknew(fname)
Xchar *fname;
X{
X extern int suppress_creation_message;
X int fd;
X
X if (!suppress_creation_message)
X {
X if ((fd = open(fname,O_RDONLY)) < 0)
X {
X fprintf(stderr,"Creating IFF CAT archive '%s'\n",fname);
X }
X else
X close(fd);
X }
X}
X
X/* open_quick_append
X * open the archive for append, verifying that it is IFF, subtype CAT,
X * that the length in the header matches the length of the file and
X * such. Note that this has been wrapped into a better OpenCAT routine
X * but I have not fixed open_quick_append to call it yet.
X */
Xint open_quick_append(fname)
Xchar *fname;
X{
X ChunkHeader mychunkheader;
X long filesize;
X int fd;
X
X /* If I can't open the archive read only, it doesn't exist, so
X * create it
X */
X if ((fd = open(fname,O_RDONLY)) < 0)
X {
X if ((fd = create_archive(fname,ID_MISC)) < 0)
X {
X perror(fname);
X return(-1);
X }
X }
X else
X {
X /* read the IFF header and validate we've got a CAT */
X if (read(fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader))
X {
X perror(fname);
X fprintf(stderr,"couldn't read chunk header\n");
X return(-1);
X }
X
X if (mychunkheader.ckID != ID_CAT)
X {
X fprintf(stderr,"file '%s' is not an IFF CAT archive\n",fname);
X return(-1);
X }
X
X /* verify that the header's filesize matches the file's size */
X if ((filesize = lseek(fd,0,2)) == -1)
X {
X perror(fname);
X return(-1);
X }
X
X if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize)
X {
X fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",fname);
X fprintf(stderr,"I'm assuming it's OK and using file size.\n");
X }
X
X /* ok, reopen the file for append and return the fd */
X close(fd);
X if ((fd = open(fname,O_RDWR|O_APPEND)) < 0)
X {
X perror(fname);
X return(-1);
X }
X }
X return(fd);
X}
X
X#define COPY_BUFFER_SIZE 32768
X
Xchar *copy_buffer = 0;
X
X/* append_file_to_archive
X *
X * this routine copies IFF file named by fname to the CAT archive known
X * by it's open-for-append fd.
X */
Xappend_file_to_archive(fname,archive_fd)
Xchar *fname;
Xint archive_fd;
X{
X char *basename_ptr;
X int bytes, i;
X int infd, fnamelen, basenamelen;
X ULONG chunkid, subtype;
X long chunksize, new_chunk_address;
X ULONG subchunkid;
X long subchunksize, placeholder, calculated_chunk_size, inputfilesize;
X
X /* seek to the end of the archive */
X lseek(archive_fd,0,2);
X
X /* open the file to appended to the archive, read only */
X if ((infd = open(fname,O_RDONLY)) == -1)
X {
X perror(fname);
X return(0);
X }
X
X /* get the filesize of the input file and relocate back to the start
X * of it */
X inputfilesize = lseek(infd,0,2);
X lseek(infd,0,0);
X
X /* get the ID and size of the next chunk */
X if ((chunkid = nextchunk(infd,&chunksize,&inputfilesize)) == 0)
X {
X fprintf(stderr,"couldn't get header chunk from file %s\n",fname);
X close(infd);
X return(0);
X }
X
X /* if the header isn't CAT, FORM or LIST, don't copy it */
X if (chunkid != ID_CAT && chunkid != ID_FORM && chunkid != ID_LIST)
X {
X fprintf(stderr,"file %s is not an IFF CAT, FORM or LIST, ignored\n",fname);
X close(infd);
X return(0);
X }
X
X /* kludgily get the subtype - for FORMs, LISTs & CATs it's the
X * first 4 bytes of the chunk data. These are included in chunksize
X */
X if (read(infd,&subtype,4) != 4)
X {
X perror("copy subtype");
X return(0);
X }
X inputfilesize -= 4;
X
X /* record where we are in the archive so we can rewrite the header
X * which we'll need to do if we add an IFAR chunk */
X new_chunk_address = lseek(archive_fd,0L,1) + 4;
X
X /* write in the chunk ID and the subtype - the program will
X * rewrite the length when we know for sure how long the
X * chunk is */
X if (!WriteChunk(archive_fd,chunkid,&subtype,4))
X {
X perror("append WriteChunk");
X return(0);
X }
X
X /* keep track of the size of the FORM, CAT or LIST we're reading
X * through. We start with 4, the size of subtype written above */
X calculated_chunk_size = 4;
X
X fnamelen = strlen(fname);
X
X /* if the filename includes a path, use only the base portion */
X basename_ptr = basename(fname);
X basenamelen = strlen(basename_ptr);
X
X /* write an IFAR chunk, it's the basename determined above,
X * and our handle for it */
X if (!WriteChunk(archive_fd,ID_IFAR,basename_ptr,basenamelen))
X return(0);
X
X /* add size of the chunk header and the length of the chunk
X * body to the calculated chunk size. If the number is odd,
X * increment it by one as the IFF spec requires odd-sized
X * chunks to be one-null-padded to make them even. Note
X * that WriteChunk took care of it for the actual data written
X */
X calculated_chunk_size += sizeof(ChunkHeader) + basenamelen;
X if (basenamelen & 1)
X calculated_chunk_size++;
X
X /* for all remaining chunks inside the FORM, LIST or CAT that
X * we're adding to the archive, */
X while ((subchunkid = nextchunk(infd,&subchunksize,&inputfilesize)) != 0)
X {
X /* if it's an IFAR chunk, discard it */
X if (subchunkid == ID_IFAR)
X skipchunk(infd,subchunksize,&inputfilesize);
X else
X {
X calculated_chunk_size += subchunksize + sizeof(ChunkHeader);
X if (subchunksize & 1)
X calculated_chunk_size++;
X
X /* write the chunk header for the embedded chunk we're copying */
X if (!WriteChunkHeader(archive_fd,subchunkid,subchunksize))
X return(0);
X
X /* now copy the embedded chunk's data */
X copychunkbytes(infd,archive_fd,subchunksize,&inputfilesize);
X }
X }
X /* get current position in the archive, seek back to the header of the
X * FORM, CAT or LIST we just copied (into the archive) and write in the
X * correct chunk size, then seek back to where we were
X */
X
X placeholder = lseek(archive_fd,0L,1);
X lseek(archive_fd,new_chunk_address,0);
X if (write(archive_fd,&calculated_chunk_size,4) != 4)
X {
X perror("archive subheader rewrite");
X fprintf(stderr,"archive is blown.\n");
X close(infd);
X return(0);
X }
X /* return to previous place in archive, close file we copied and
X * return 'success' */
X lseek(archive_fd,placeholder,0);
X close(infd);
X return(1);
X}
X
X/* rewrite_archive_header - write (filesize - sizeof(ChunkHeader)) into
X * CAT archive's header, assumes file is open for write
X */
Xrewrite_archive_header(fd)
X{
X long filesize;
X
X /* get filesize by seeking to EOF */
X if ((filesize = lseek(fd,0,2)) == -1)
X {
X perror("archive");
X return(0);
X }
X
X /* go back to the beginning of the file */
X if (lseek(fd,0,0) == -1)
X {
X perror("archive cleanup seek");
X return(0);
X }
X
X if (!WriteChunkHeader(fd,ID_CAT,(filesize - sizeof(ChunkHeader))))
X {
X perror("archive cleanup");
X return(0);
X }
X
X return(1);
X}
X
X/* the copy buffer cleanup routine, it frees the copy buffer memory if
X * the buffer has been allocated
X */
Xvoid cleanup_copy_buffer()
X{
X if (copy_buffer)
X FreeMem(copy_buffer,COPY_BUFFER_SIZE);
X}
X
X/* copychunkbytes
X *
X * copy nbytes from infd to outfd, subtracting that amount from
X * the number of filebytes left within the virtual chunk, as given
X * by the address of that variable
X */
Xcopychunkbytes(infd,outfd,nbytes,filebytes_left_ptr)
Xint infd, outfd;
Xlong nbytes, *filebytes_left_ptr;
X{
X int copysize, odd;
X
X /* if we haven't allocated copy_buffer, allocate it and add it's cleanup
X * routine (cleanup_copy_buffer) to the cleanup list */
X if (!copy_buffer)
X {
X if ((copy_buffer = (char *)AllocMem(COPY_BUFFER_SIZE,MEMF_FAST)) == (char *)NULL)
X panic("couldn't allocate copy buffer");
X
X add_cleanup(cleanup_copy_buffer);
X }
X
X /* if nbytes of copying requested exceeds the virtual EOF (end of
X * the chunk's metachunk), truncate the chunk
X */
X if (nbytes > *filebytes_left_ptr)
X {
X fprintf(stderr,"copychunkbytes: chunk size exceeds size of superchunk - truncating\n");
X nbytes = *filebytes_left_ptr;
X }
X
X /* find out if the length of the chunk is odd or not - we'll need
X * it later to see if we need to write a pad byte
X */
X odd = (nbytes & 1);
X
X /* do the copy, breaking it up into multiple COPY_BUFFER_SIZE sized
X * portions, if neccessary */
X while (nbytes > 0)
X {
X copysize = (nbytes > COPY_BUFFER_SIZE) ? COPY_BUFFER_SIZE : nbytes;
X
X if (read(infd,copy_buffer,copysize) != copysize)
X {
X perror("copybytes input");
X fprintf(stderr,"archive is blown.\n");
X close(infd);
X return(0);
X }
X if (write(outfd,copy_buffer,copysize) != copysize)
X {
X perror("copybytes output");
X fprintf(stderr,"archive is blown.\n");
X close(infd);
X return(0);
X }
X /* update bytes left in chunk and in chunk's metachunk */
X nbytes -= copysize;
X *filebytes_left_ptr -= copysize;
X }
X
X /* done with copy - if number of bytes copied was odd, read and
X * discard the pad byte, write out a pad byte and update the
X * virtual EOF (end of chunk) */
X if (odd)
X {
X if (read(infd,copy_buffer,1) != 1)
X perror("copychunkbytes: failed to skip input byte");
X assert(*copy_buffer == '\0');
X write(outfd,copy_buffer,1);
X (*filebytes_left_ptr)--;
X }
X}
X
X/* create an archive by opening it, writing a CAT header, and returning the
X * file descriptor if it succeeded or -1 otherwise
X */
Xint create_archive(archive_name,subtype)
Xchar *archive_name;
XULONG subtype;
X{
X int archive_fd;
X
X if ((archive_fd = open(archive_name, O_RDWR|O_CREAT|O_TRUNC)) == -1)
X {
X perror(archive_name);
X return(-1);
X }
X
X if (!WriteCATheader(archive_fd))
X {
X fprintf("create_archive: couldn't write CAT chunkheader of new archive\n");
X return(-1);
X }
X
X /* success, return the archive fd */
X return(archive_fd);
X}
X
X/* end of create.c */
X
//END_OF_FILE
echo x - delete.c
sed 's/^X//' > delete.c << '//END_OF_FILE'
X/* iffar - IFF CAT archiver delete functions
X
X By Karl Lehenbauer, version 1.2, release date 5/9/88.
X This code is released to the public domain.
X See the README file for more information.
X
X*/
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include "assert.h"
X#include "iff.h"
X
Xextern ULONG nextchunk();
X
Xint delete_entries(archive_name,fnames,nfiles)
Xchar *archive_name;
Xchar *fnames[];
Xint nfiles;
X{
X int old_archive_fd, new_archive_fd;
X ULONG cat_type, chunkid, innerchunkid, subtype;
X long chunksize, innerchunksize, filesize;
X char textbuf[128], old_archive_name[128];
X int i, delete_file, file_bytes;
X
X extern int verbose;
X
X /* rename the archive to its old name concatenated with ".old"
X */
X sprintf(old_archive_name,"%s.old",archive_name);
X unlink(old_archive_name);
X rename(archive_name,old_archive_name);
X
X if ((old_archive_fd = OpenCAT(old_archive_name,&cat_type,&filesize)) == -1)
X {
X fprintf(stderr,"Can't open archive '%s'\n",old_archive_name);
X return(0);
X }
X
X if ((new_archive_fd = create_archive(archive_name,ID_MISC)) < 0)
X return(0);
X
X while ((chunkid = nextCATchunk(old_archive_fd,&subtype,&textbuf[0],&chunksize,&filesize)) != 0L)
X {
X /* if the chunk type isn't FORM, CAT or LIST, copy it across
X * without looking at it */
X if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST)
X {
X if (!WriteChunkHeader(new_archive_fd,chunkid,chunksize))
X return(0);
X copychunkbytes(old_archive_fd,new_archive_fd,chunksize,&filesize);
X }
X
X /* search to see if this chunk's name is one specified in fnames,
X * an array of pointer to char strings */
X
X delete_file = 0;
X for (i = 0; i < nfiles; i++)
X {
X if (!strnicmp(fnames[i],textbuf, 128))
X {
X delete_file = 1;
X break;
X }
X }
X /* if we did decide to delete it, skip it */
X if (delete_file)
X {
X if (verbose)
X fprintf(stderr,"deleting %s\n",textbuf);
X
X if (!skipchunk(old_archive_fd,chunksize,&filesize))
X {
X fprintf(stderr,"delete: skipchunk failed\n");
X return(0);
X }
X }
X else /* we want to copy it */
X {
X if (!WriteCATentry(new_archive_fd,textbuf,chunkid,subtype,chunksize))
X return(0);
X
X copychunkbytes(old_archive_fd,new_archive_fd,chunksize,&filesize);
X }
X }
X
X /* write the right length in for the header */
X rewrite_archive_header(new_archive_fd);
X
X /* close the old and new archive files and return success */
X close(old_archive_fd);
X close(new_archive_fd);
X return(1);
X}
X
X/* end of extract.c */
X
//END_OF_FILE
echo x - extract.c
sed 's/^X//' > extract.c << '//END_OF_FILE'
X/* iffar - IFF CAT archiver extractor functions
X
X By Karl Lehenbauer, version 1.2, release date 5/9/88.
X This code is released to the public domain.
X See the README file for more information.
X
X*/
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include "assert.h"
X#include "iff.h"
X
X#define ID_NAME MakeID('N','A','M','E')
X#define ID_AUTH MakeID('A','U','T','H')
X#define ID_ANNO MakeID('A','N','N','O')
X#define ID_Copyright MakeID('(','c',')',' ')
X
Xextern long lseek();
X
Xextern ULONG nextchunk();
X
Xint extract(archive_name,fnames,nfiles)
Xchar *archive_name;
Xchar *fnames[];
Xint nfiles;
X{
X int archive_fd, outfd;
X ULONG cat_type, chunkid, innerchunkid, subtype;
X long chunksize, innerchunksize, filesize;
X char textbuf[256], *extract_name;
X long placeholder;
X int do_extract, i;
X
X extern int verbose;
X
X if ((archive_fd = OpenCAT(archive_name,&cat_type,&filesize)) == -1)
X {
X fprintf(stderr,"Can't open archive '%s'\n",archive_name);
X return(0);
X }
X
X while ((chunkid = nextCATchunk(archive_fd,&subtype,&textbuf[0],&chunksize,&filesize)) != 0L)
X {
X /* if the chunk type isn't FORM, CAT or LIST, skip it 'cuz it
X * isn't anything we know how to extract */
X if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST)
X {
X if (!skipchunk(archive_fd,chunksize,&filesize))
X {
X perror(archive_fd);
X fprintf(stderr,"extract: skipchunk failed\n");
X return(0);
X }
X break;
X }
X
X /* if no extract names (nfiles == 0) were specified, extract all
X * files, so extract this one, else search to see if this name
X * is one specified in fnames, an array of pointers to char
X * strings */
X do_extract = 0;
X if (nfiles == 0)
X {
X do_extract = 1;
X extract_name = textbuf;
X }
X else
X {
X for (i = 0; i < nfiles; i++)
X {
X if (!strnicmp(basename(fnames[i]),textbuf,128))
X {
X do_extract = 1;
X extract_name = fnames[i];
X break;
X }
X }
X }
X /* if we did decide to extract it, extract it */
X if (do_extract)
X {
X if (verbose)
X fprintf(stderr,"extracting %s\n",extract_name);
X
X /* create, open and truncate the extract file */
X if ((outfd = open(extract_name,O_RDWR|O_CREAT|O_TRUNC)) == -1)
X {
X perror(textbuf);
X }
X else
X {
X if (!WriteSuperChunkHeader(outfd,chunkid,subtype,chunksize))
X return(0);
X
X copychunkbytes(archive_fd,outfd,chunksize,&filesize);
X close(outfd);
X }
X
X /* if they specified files on the command line, null them
X * out after we retrieve them, later we'll make a pass
X * through and complain about any we don't find with
X * their first bytes nulled out
X */
X if (nfiles != 0)
X *fnames[i] = '\0';
X }
X /* else we don't want to extract it, so skip it */
X else if (!skipchunk(archive_fd,chunksize,&filesize))
X {
X perror(archive_fd);
X fprintf(stderr,"extract: skipchunk failed\n");
X return(0);
X }
X }
X
X /* complain about any files named that we didn't extract */
X for (i = 0; i < nfiles; i++)
X {
X if (*fnames[i] != '\0')
X fprintf(stderr,"%s: no such archive entry\n",fnames[i]);
X }
X
X /* close the archive file and return success */
X close(archive_fd);
X return(1);
X}
X
X/* end of extract.c */
X
//END_OF_FILE
echo x - iff.c
sed 's/^X//' > iff.c << '//END_OF_FILE'
X/* iffar - IFF CAT archiver, IFF support functions
X
X By Karl Lehenbauer, version 1.2, release date 5/9/88
X This code is released to the public domain.
X See the README file for more information.
X
X*/
X
X/* culled general purpose IFF file cracking routines for Karl's
X * IFF Stuff by Karl Lehenbauer, based originally on public domain IFF
X * code from Electronic Arts, 2/24/88
X */
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include "assert.h"
X
X#include "iff.h"
X
Xextern long lseek();
X
Xextern ULONG nextchunk();
X
X/* print a chunkID to stderr */
XPutID(id)
XID id;
X{
X fprintf(stderr,"%c%c%c%c",
X (char)((id>>24L) & 0x7f),
X (char)((id>>16L) & 0x7f),
X (char)((id>>8) & 0x7f),
X (char)(id & 0x7f) );
X}
X
XUBYTE *MyAllocMem(bytes, type)
XULONG bytes, type;
X{
X UBYTE *tmp;
X tmp = AllocMem(bytes, type);
X return tmp;
X}
X
X/* return chunktype of next chunk */
X/* every time nextchunk is executed and returns that it found a chunk,
X * either readchunk or skipchunk must be called and only one time!
X */
XULONG nextchunk(fd,chunksize,chunk_bytes_left)
Xint fd;
Xlong *chunksize, *chunk_bytes_left;
X{
X int sawsize, i, blown = 0;
X ChunkHeader mychunkheader;
X char checkchar;
X
X /* if chunk_bytes_left is zero, we obey it as a virtual EOF, so
X * return 0 */
X if (*chunk_bytes_left == 0)
X return(0);
X
X /* read the next chunk header */
X if ((sawsize = read(fd,&mychunkheader,sizeof(mychunkheader))) !=
X sizeof(mychunkheader))
X {
X if (sawsize != 0)
X fprintf(stderr,"Something's wrong with nextchunk! (sawsize %d)\n", sawsize);
X *chunksize = 0;
X return(0);
X }
X
X#ifdef MAJORDEBUG
X fputs("nextchunk: next chunk '",stderr);
X PutID(mychunkheader.ckID);
X fprintf(stderr,"', size %d, parent bytes left %d\n",mychunkheader.ckSize,*chunk_bytes_left);
X#endif
X
X *chunksize = mychunkheader.ckSize;
X
X /* see if chunk ID looks OK */
X for (i = 0; i < 4; i++)
X {
X checkchar = (mychunkheader.ckID >> (i * 8)) & 0xff;
X if (!isprint(checkchar))
X {
X if (!blown)
X {
X blown = 1;
X fprintf(stderr,"nextchunk: chunk ID contains an unprintable character (0x%x)\n",checkchar);
X }
X break;
X }
X }
X
X /* see if chunk length is reasonable */
X if ((mychunkheader.ckSize < 0) || (mychunkheader.ckSize > MAXCHUNKSIZE))
X {
X fprintf(stderr,"nextchunk: chunk length of %ld is unreasonable\n",mychunkheader.ckSize);
X blown = 1;
X }
X
X if (blown)
X {
X fprintf(stderr,"nextchunk: I either got lost or the archive is blown\n");
X return(0);
X }
X
X /* square up the bytes left in the chunk by the size of a chunk header,
X * eight bytes. We leave it to the caller to subtract the size of the
X * body of the chunk by calling skipchunk or readchunk
X */
X *chunk_bytes_left -= sizeof(mychunkheader);
X
X if (*chunk_bytes_left < 0)
X {
X fprintf("nextchunk: chunk overran its parent by %d bytes\n",(0-*chunk_bytes_left));
X *chunksize = 0;
X *chunk_bytes_left = 0;
X return(0);
X }
X
X return(mychunkheader.ckID);
X}
X
X/* read next chunk into buffer supplied, size must be value returned by
X * nextchunk
X * zero is returned on failure, one on success
X */
Xreadchunk(fd,buf,size,chunk_bytes_left)
Xint fd;
Xchar *buf;
XLONG size, *chunk_bytes_left;
X{
X *chunk_bytes_left -= size;
X
X if (*chunk_bytes_left < 0)
X {
X fprintf(stderr,"readchunk: chunk requested passed the end of its parent chunk\n");
X *chunk_bytes_left = 0;
X return(0);
X }
X
X if (read(fd,buf,size) != size)
X {
X perror("smus file");
X fputs("LoadSMUS: read of IFF chunk failed\n",stderr);
X return(0);
X }
X
X /* odd-length chunks have a trailer byte - skip it */
X if (size & 1)
X {
X lseek(fd,1L,1);
X (*chunk_bytes_left)--;
X }
X return(1);
X}
X
X/* skip non-header portion of chunk, chunksize must have been returned
X * by nextchunk
X * returns 1 on success, 0 on failure
X */
Xskipchunk(fd,chunksize,chunk_bytes_left)
Xint fd;
XLONG chunksize, *chunk_bytes_left;
X{
X *chunk_bytes_left -= chunksize;
X if (chunksize & 1)
X (*chunk_bytes_left)--;
X if (*chunk_bytes_left < 0)
X {
X fprintf(stderr,"skipchunk: chunk size passes end of parent chunk's data by %d bytes\n",0 - *chunk_bytes_left);
X return(0);
X }
X /* skip over chunk data and skip an extra byte if length is odd */
X lseek(fd,(long)chunksize,1);
X if (chunksize & 1)
X lseek(fd,1L,1);
X return(1);
X}
X
X/* OpenIFF
X * given file name, open the IFF file.
X * read the header, return failure if it's not a FORM
X * (someday we'll handle the more complex types)
X * read the form type, return failure if it's not the type requested
X * success, return the file descriptor
X */
X
Xint OpenIFF(fname,expected_formtype,length_ptr)
Xchar *fname;
XLONG expected_formtype;
XLONG *length_ptr;
X{
X int iffile;
X ChunkHeader chunkhead;
X LONG formtype;
X
X /* open the file */
X if ((iffile = open(fname, O_RDONLY)) < 0)
X {
X fprintf(stderr,"OpenIFF: can't open IFF SMUS file %s\n",fname);
X perror(fname);
X return(-1);
X }
X
X /* get the length */
X *length_ptr = lseek(iffile,0,2);
X lseek(iffile,0,0);
X
X /* read the header chunk */
X if (read(iffile, &chunkhead, sizeof(chunkhead)) < 0)
X {
X fprintf(stderr,"OpenIFF: initial read from IFF file %s failed!\n",fname);
X return(-1);
X }
X
X /* return if the header chunk doesn't say it's IFF FORM */
X if (chunkhead.ckID != ID_FORM)
X {
X fprintf(stderr,"OpenIFF: File %s isn't IFF, is too complex, or doesn't start with FORM\n",fname);
X return(-1);
X }
X /* fprintf(stderr,"OpenIFF: FORM found, size is %d\n",chunkhead.ckSize); */
X
X /* read the form type */
X read(iffile, &formtype, sizeof(formtype));
X
X /* return if the form type isn't the type requested */
X if (formtype != expected_formtype)
X {
X fprintf(stderr,"OpenIFF: File %s is IFF ");
X PutID(formtype);
X fprintf(stderr," rather than the requested ");
X PutID(expected_formtype);
X fprintf(stderr,"\n");
X return(-1);
X }
X return(iffile);
X}
X
X/* read chunks until one of type chunktype is found or EOF
X * note that after a successful call to chunkuntil,
X * skipchunk or readchunk must be performed or the IFF reading
X * software will get lost on the next nextchunk
X * chunksize is returned on success, -1 otherwise
X * The caller should probably check the return explicitly for -1.
X * If checking only for less than zero, chunks larger than
X * two gigabytes will cause your code to break.
X */
X
XLONG chunkuntil(fd,chunktype,file_bytes_left)
Xint fd;
XULONG chunktype;
Xlong *file_bytes_left;
X{
X ULONG currentchunk;
X LONG chunksize;
X
X while ((currentchunk = nextchunk(fd,&chunksize,file_bytes_left)) != NULL)
X {
X if (currentchunk == chunktype)
X return(chunksize);
X skipchunk(fd,chunksize,file_bytes_left);
X }
X return(0);
X}
X
X/* OpenCAT - Open an IFF CAT archive */
X
X/* OpenCAT
X * Open an IFF CAT archive, insuring that the file starts with an
X * IFF CAT header and that the length in the header is valid.
X * Return the CAT subtype, file descriptor and length, leaving the
X * file pointed at the start of the first subchunk
X */
X
Xint OpenCAT(archive_name,subtype_ptr,length_ptr)
Xchar *archive_name;
XULONG *subtype_ptr, *length_ptr;
X{
X ChunkHeader mychunkheader;
X int archive_fd;
X long start_of_body, filesize;
X long placeholder;
X
X if ((archive_fd = open(archive_name,O_RDONLY)) == -1)
X {
X /* fprintf(stderr,"Can't open archive '%s'\n",archive_name); */
X return(-1);
X }
X
X if (read(archive_fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader))
X {
X perror(archive_name);
X fprintf(stderr,"couldn't read chunk header\n");
X return(-1);
X }
X
X if (mychunkheader.ckID != ID_CAT)
X {
X fprintf(stderr,"file '%s' is not an IFF CAT archive\n",archive_name);
X return(-1);
X }
X
X if (read(archive_fd,subtype_ptr,sizeof(subtype_ptr)) != sizeof(subtype_ptr))
X {
X fprintf(stderr,"error reading archive header - subtype\n");
X return(-1);
X }
X
X /* save location of current start of body */
X if ((start_of_body = lseek(archive_fd,0,1)) == -1)
X {
X perror(archive_name);
X return(-1);
X }
X
X /* seek to the end to get the size */
X if ((filesize = lseek(archive_fd,0,2)) == -1)
X {
X perror(archive_name);
X return(-1);
X }
X
X /* see if the shoe fits */
X if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize)
X {
X fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",archive_name);
X fprintf(stderr,"I'm assuming it's blown.\n");
X return(-1);
X }
X
X /* go back to the start of the IFF CAT archive's data */
X if (lseek(archive_fd,start_of_body,0) == -1)
X {
X perror(archive_name);
X return(-1);
X }
X
X /* it worked store filesize in location pointed to by 'length'
X * and return the archive file's file descriptor
X */
X *length_ptr = filesize;
X return(archive_fd);
X}
X
X/* end of OpenCAT */
X
X/* nextcat - read header info for the next entry in an IFF CAT */
X
X/* nextCATchunk
X *
X * given fd, read into IFF file.
X * if we're not at a FORM, CAT or LIST, print the chunk type if verbose,
X * then skip the chunk
X * if we are at a FORM, CAT or LIST, read the subtype and return it
X * via the argument subtype_ptr.
X * if the next chunk within the embedded FORM, CAT or LIST is IFAR,
X * read the text in the IFAR chunk (file name) and write it into space
X * pointed to by argument fname_ptr.
X * return the size of the chunk in argument chunk_length_ptr.
X * update the space left in the metachunk (usually the file) of argument
X * metachunk_length_ptr
X */
X
XULONG nextCATchunk(fd,subtype_ptr,fname_ptr,chunk_length_ptr,metachunk_length_ptr)
Xint fd;
XULONG *subtype_ptr;
Xchar *fname_ptr;
XLONG *chunk_length_ptr, *metachunk_length_ptr;
X{
X ULONG cat_type, chunkid, innerchunkid;
X long chunksize, innerchunkposition, innerchunksize, filesize;
X int odd;
X
X /* null out the returned subtype and fnam */
X *subtype_ptr = 0L;
X *fname_ptr = '\0';
X
X if ((chunkid = nextchunk(fd,chunk_length_ptr,metachunk_length_ptr)) == 0L)
X return(0L);
X
X /* if the chunk type isn't FORM, CAT or LIST, return the chunkid
X */
X if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST)
X return(chunkid);
X
X /* get the chunk subtype */
X if (read(fd,subtype_ptr,4) != 4)
X {
X perror("reading subtype");
X return(0);
X }
X
X /* reduce chunksize and metachunksize by the size of the subtype */
X *chunk_length_ptr -= sizeof(ULONG);
X *metachunk_length_ptr -= sizeof(ULONG);
X
X /* sneak a peek into the embedded FORM, CAT or LIST to see
X * if the next chunk is an IFAR chunk */
X
X assert(*chunk_length_ptr > 0);
X
X /* fetch the current location in the file - we'll restore it
X * if we don't find this next chunk to be an IFAR one
X */
X innerchunkposition = lseek(fd,0L,1);
X
X /* get the type and size of the inner chunk */
X chunksize = *chunk_length_ptr;
X innerchunkid = nextchunk(fd,&innerchunksize,&chunksize);
X
X /* if it's not an fname chunk, seek back to the start of the
X * chunk and return the chunk id - master length should be OK
X */
X if (innerchunkid != ID_IFAR)
X {
X lseek(fd,innerchunkposition,0);
X return(chunkid);
X }
X
X odd = innerchunksize & 1;
X
X /* read and zero-terminate the file name (contents of IFAR chunk) */
X if (!readchunk(fd,fname_ptr,innerchunksize,&chunksize))
X {
X fprintf(stderr,"nextCATchunk: got into trouble reading chunk text\n");
X return(0);
X }
X *(fname_ptr + innerchunksize) = '\0';
X
X /* update the length of the chunk and its parent & return the chunk id
X * (nextchunk normally handles updating the length but we used different
X * variables to make restoring (in case we don't find an IFAR chunk)
X * easier
X */
X *chunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize);
X *metachunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize);
X if (odd)
X {
X (*chunk_length_ptr)--;
X (*metachunk_length_ptr)--;
X }
X return(chunkid);
X}
X
X/* end of nextCATchunk */
X
X/* end of iff.c */
//END_OF_FILE
echo x - includes.c
sed 's/^X//' > includes.c << '//END_OF_FILE'
X/* iffar - archiver includes.c - for Manx +H +I options to precompile includes */
X
X/* iffar - IFF CAT archiver includes.c for include precompile
X
X By Karl Lehenbauer, version 1.2, release date 5/9/88.
X This code is released to the public domain.
X See the README file for more information.
X
X*/
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <exec/nodes.h>
X#include <exec/lists.h>
X/* #include <devices/audio.h> */
X#include <fcntl.h>
X#include <stdio.h>
X#include <functions.h>
X
X#include "assert.h"
X#include "iff.h"
X
X/* end of includes.c */
//END_OF_FILE
echo x - main.c
sed 's/^X//' > main.c << '//END_OF_FILE'
X/* iffar - IFF CAT archiver, main()
X
X By Karl Lehenbauer, version 1.2, release date 5/9/88.
X This code is released to the public domain.
X See the README file for more information.
X
X*/
X
X/* iffar - iff 'cat' archiver, 3/26/88 */
X
X#include <stdio.h>
X#include "assert.h"
X
X/* options */
Xint verbose = 0;
Xint insert_after = 0;
Xint insert_before = 0;
Xint suppress_creation_message = 0;
X
X/* if insert_after or insert_before is set, this global has the relevant
X * name */
Xchar *location_modifier_name;
X
X/* variables */
Xint number_of_arguments; /* number of args to the program, a global */
X
X/* this'll point to the command line's command char string */
Xchar *cmdkeys;
X
X/* this'll be the name of the archive file */
Xchar *archive_name;
X
X/* this'll contain the command */
Xchar command_key;
X
X/* if a command must have some names as arguments, must_have_names can be
X * called to insure that arguments were supplied.
X * Note that what constitutes the presence of arguments varies whether
X * a positional modifier ('i', 'b' or 'a') was specified.
X */
Xmust_have_names()
X{
X if ((insert_before || insert_after) && (number_of_arguments < 5))
X {
X fprintf(stderr,"a positioning element and at least one other element must be specified for\n");
X fprintf(stderr,"the combination of options you have requested.\n");
X usage();
X }
X if (number_of_arguments < 4)
X {
X fprintf(stderr,"at least one archive element must be specified for this option\n");
X usage();
X }
X}
X
Xheader()
X{
X fprintf(stderr,"iffar - Hackercorp public domain IFF CAT archiver,\n\tVersion 1.4 (9/23/89), by Karl Lehenbauer\n\n");
X fprintf(stderr,"This program maintains archives of IFF FORM and CAT files\n");
X fprintf(stderr,"in a manner that complies with the IFF CAT specification.\n");
X}
X
X/* usage print usage text and exit */
Xusage()
X{
X fprintf(stderr,"\nUsage: iffar key [posname] afile name ...\n\n");
X fprintf(stderr,"key can be one of the following:\n");
X fprintf(stderr,"\td\tdelete\n\tr\treplace\n\tq\tquick append\n");
X fprintf(stderr,"\tt\ttable of contents\n\tx\textract\n");
X fprintf(stderr,"and zero or more of the following options:\n");
X fprintf(stderr,"\tv\tverbose\n\ta\tafter\n\ti,b\tbefore\n");
X fprintf(stderr,"\tc\tsuppress creation message\n");
X cleanup();
X exit(1);
X}
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X int i, j;
X int archive_fd, files;
X char *nameptr;
X char **entryname_pointers;
X int entrycount;
X
X number_of_arguments = argc;
X
X if (argc < 3)
X {
X header();
X usage();
X }
X
X /* assign nice variable names to various command line arguments */
X cmdkeys = argv[1];
X command_key = *cmdkeys;
X archive_name = argv[2];
X location_modifier_name = (char *)NULL;
X entryname_pointers = &argv[3];
X entrycount = argc - 3;
X
X /* figure out any modifiers specified to the main function requested */
X for (i = 1; i < strlen(cmdkeys); i++)
X {
X switch(cmdkeys[i])
X {
X case 'v': /* verbose selected, set verbose flag */
X verbose = 1;
X break;
X
X /* 'after' option, make sure they've selected 'r' or 'm' as
X * the main command. Also, make sure they haven't already
X * chosen the insert_before option. After that, we're OK,
X * so twiddle parameters and break
X */
X case 'a':
X if (command_key != 'r' && command_key != 'm')
X {
X fprintf(stderr,"i, b and a modifiers are only good for r and m options\n");
X usage();
X }
X if (insert_before)
X {
X fprintf(stderr,"you can't select 'insert before' and 'insert after'\n");
X usage();
X }
X insert_after = 1;
X location_modifier_name = argv[3];
X entryname_pointers = &argv[4];
X entrycount = argc - 4;
X break;
X
X /* 'insert before' option, make sure they've selected 'r' or
X * 'm' as the main command. Also, make sure they haven't already
X * chosen the insert_after option. After that, we're OK,
X * so twiddle parameters and break
X */
X case 'i': /* insert; before */
X case 'b':
X if (command_key != 'r' && command_key != 'm')
X {
X fprintf(stderr,"i, b and a modifiers are only good for r and m options\n");
X usage();
X }
X if (insert_after)
X {
X fprintf(stderr,"you can't select 'insert before' and 'insert after'\n");
X usage();
X }
X insert_before = 1;
X location_modifier_name = argv[3];
X entryname_pointers = &argv[4];
X entrycount = argc - 4;
X break;
X
X case 'c': /* supress creation message */
X suppress_creation_message = 1;
X break;
X
X default: /* unrecognized option */
X fprintf(stderr,"option '%c' unrecognized\n",cmdkeys[i]);
X usage();
X }
X }
X
X /* now execute the major command */
X
X switch(command_key)
X {
X case 'd': /* delete */
X must_have_names();
X delete_entries(archive_name,entryname_pointers,entrycount);
X break;
X
X /* replace - if the archive doesn't exist, fall through to
X * quick append */
X case 'r':
X must_have_names();
X if ((archive_fd = open(archive_name,O_RDONLY)) != -1)
X {
X close(archive_fd);
X replace_entries(archive_name,entryname_pointers,entrycount);
X break;
X }
X case 'q': /* quick append */
X must_have_names();
X checknew(archive_name);
X if ((archive_fd = open_quick_append(archive_name)) == -1)
X break;
X quickappend_entries(archive_fd,entryname_pointers,entrycount);
X break;
X case 't': /* table of contents */
X table_of_contents(archive_name);
X break;
X
X case 'm': /* move */
X fprintf(stderr,"move option not implemented\n");
X must_have_names();
X break;
X case 'x': /* extract */
X extract(archive_name,entryname_pointers,entrycount);
X break;
X
X default:
X fprintf(stderr,"requested command (%c) is invalid\n",command_key);
X usage();
X }
X cleanup();
X exit(0);
X}
X
X/* end of main.c */
//END_OF_FILE
echo "End of archive."
# end of archive.
exit 0
--
-- uunet!sugar!karl "There is hopeful symbolism in the fact that
-- flags do not wave in a vacuum." -- Arthur C. Clarke
-- Usenet access: (713) 438-5018