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