Path: utzoo!attcan!uunet!wyse!vsi1!ames!haven!purdue!i.cc.purdue.edu!j.cc.purdue.edu!ain From: ain@j.cc.purdue.edu (Patrick White) Newsgroups: comp.sources.amiga Subject: iffcat (sources 1 of 2) Keywords: IFF, cat, tested Message-ID: <7726@j.cc.purdue.edu> Date: 8 Sep 88 02:35:14 GMT Organization: PUCC Land, USA Lines: 1103 Approved: ain@j.cc.purdue.edu (Patrick White) Submitted by: nuchat!sugar!karl@uunet.uu.net Summary: Uses IFF form(?) CAT to pack several IFF files into one Poster Boy: Patrick White (ain@j.cc.purdue.edu) Archive Name: sources/amiga/volume5/iffcat.s.sh1.Z Tested NOTES: Re-shar'ed. -- Pat White (co-moderator comp.sources/binaries.amiga) ARPA/UUCP: j.cc.purdue.edu!ain BITNET: PATWHITE@PURCCVM PHONE: (317) 743-8421 U.S. Mail: 320 Brown St. apt. 406, West Lafayette, IN 47906 [archives at: j.cc.purdue.edu.ARPA] ======================================== # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # README # Makefile # assert.h # cleanup.c # create.c # delete.c # extract.c # This archive created: Tue Sep 6 13:38:14 1988 # By: Patrick White (PUCC Land, USA) cat << \SHAR_EOF > README iffar - IFF CAT Archiver, Release 1.2, 5/9/88 Written by: Karl Lehenbauer 3918 Panorama Missouri City, TX 77459 usenet: ..uunet!nuchat!sugar!karl or ..bellcore!tness7!sugar!karl BIX: kelehen All liability is disclaimed! This code is free. We do not have a contract. This software, source and binary, is released to the public domain, 5/9/88. I ask that you retain my name as the original author in the source and documentation if you redistribute this and that I be credited in the user manual if iffar is redistributed with a commercial product. Regards, Karl Lehenbauer @ The Hacker's Haven, Missouri City, Texas, 5/9/88 --------------------------------------------------------------------- NAME iffar - IFF CAT archiver SYNOPSIS iffar option [posname] archive_file [IFF_file] ... DESCRIPTION Iffar maintains archives of Interchange File Format (IFF) FORM, CAT and LIST files in a manner that complies with the IFF CAT specification. IFF CAT archives should be portable to different machines. No promises. The option string must start with 'd', 'q', 'r', 't' or 'x' and may additionally have modifiers of 'a', 'b', 'c', 'i' or 'v'. Not all modifiers are valid with all options. The options are: 'd' Delete named IFF files from the CAT archive. 'q' Quickly append named IFF files to the CAT archive. 'r' Replace named IFF files in the CAT archive; append new ones. 't' Print a table of contents of the CAT archive. 'x' Extract named IFF files from the CAT archive. If no names are specified, all files are extracted. The modifiers are: 'a' "after", replace or append IFF files after entry in CAT archive named by posname 'b' "before", replace or append IFF files before entry in CAT archive named by posname 'c' Do not print a message indicating archive is being created when it must be created. 'i' a synonym for "before" 'v' print verbose description of all activity For table of contents, prints IDs, and lengths of chunks within the IFF file chunks in the CAT archive. It prints the contents of chunks that it knows to be textual and short. On all operations that modify the archive, except for "quick append", the archive will be renamed with a ".old" extension and a new archive will be created. The "quick append" option causes the named files to be added to the end of the archive without rewriting the archive or looking to see if the entry already exists. This is to avoid quadratic behavior when building up an archive one or just a few entries at a time. Wildcards of the semi-Unix-type as supplied by Manx are provided. Only the basename of the specified IFF filenames will be used for element names within the archive. In other words, pathnames are stripped from filenames to create the archive element name. This is also true for extracts. EXAMPLES iffar x foo ram:t/bar would extract element "bar" from archive "foo" into file "ram:t/bar" iffar ra sounds Cabasa dh0:sounds/Snare would replace a FORM, CAT or LIST named Snare in the archive with dh0:sounds/Snare, placing Snare directly after Cabasa. If Cabasa is not found in the archive, Snare is placed at the end. BUGS The archive will be corrupted if a write error (including running out of disk space) occurs during "quick append" mode and there won't be a ".old" backup file. Running out of disk space leaves corrupted archives. For all options but "q" the ".old" backup file will contain the archive in its state prior to the run that blew it up. The program should delete the corrupted archive and restore the ".old" file, but it doesn't. IMPLEMENTATION NOTES Iffar is written to run under Manx Aztec C 3.6a for the Amiga under AmigaDOS version 1.2. My IFF archiving routines were written to be well-behaved from an IFF point of view; that is, they try to respect the virtual end-of-file defined by the size field of a chunk's header when diving through the chunk's subchunks. It makes for more work to use the routines, but adds some certainty that the program is working properly. I don't know. The IFF spec requests that we do, so I do. Note that when converting your code to use a CAT file instead of reading several FORM files (it's about twice as fast for an application of mine involving about 25 files from two to ten kilobytes each, reading them in from floppy.), your code needs to either be driven by the files read from the CAT (you search a list of names you're looking for when you see a FORM and load what you want) or your code has to know what's coming from the CAT specifically. I'd prefer that you do the former, of course. The table of contents routines (toc.c) provide a reasonable template for your CAT-reading application. NOTES FOR THOSE WISHING TO PORT THE ARCHIVER Knock yourselves out. The code is written using standard C library calls, with the exception of calls to "scdir", which Manx uses as a means of expanding wildcards. If you get it to work on a different system, such as Unix System/V, please send me a copy, preferably with #ifdefs so the Manx stuff still works. Ultimately, I think an alternative "_main" startup routine should be written for the Amiga that expands wildcards inline to provide an argc and argv as they would look on Unix after the shell had expanded all the wildcards. REVISION HISTORY 4/25/88, version 1.1 Initial version - never distributed, as far as I know 5/9/88, version 1.2 Made 'r' (replace) option create the archive if it isn't there. Fixed bug that caused the IFF reader to get lost when doing a verbose table of contents on certain archives. Flattened the source directory structure. Updated the documentation. ----------------------------------------------------------------------------- SHAR_EOF cat << \SHAR_EOF > Makefile # iffar - IFF CAT archiver, makefile # # By Karl Lehenbauer, version 1.2, release date 5/9/88. # This code is released to the public domain. # See the README file for more information. # # This makefile is for Manx Aztec C 3.6a for the Commodore Amiga computer. # It probably works under 3.4, but I haven't tried it. # #CFLAGS= -n +p -DDEBUG +Iincludes.pre CFLAGS= +p +Iincludes.pre HFILES= assert.h iff.h .c.o: cc $(CFLAGS) $*.c all: iffar say "ready" clean: -delete #?.o -delete #?.bak -delete includes.pre scratch: clean all iffar: includes.pre main.o cleanup.o create.o toc.o iff.o \ extract.o delete.o replace.o quickappend.o misc.o ln +q -o iffar main.o cleanup.o create.o toc.o iff.o extract.o \ delete.o replace.o quickappend.o misc.o -lcl32 includes.pre: $(HFILES) -delete #?.o cc +p +Hincludes.pre includes.c SHAR_EOF cat << \SHAR_EOF > assert.h /* hackercorp - hackercore assert file for Amiga * kel 12/87 v1.0 */ #ifndef NODEBUG #ifndef stderr #include #endif #define assert(x) if (!(x)) {fprintf(stderr,"Assertion failed: x, file %s, line %d\n",__FILE__,__LINE__); cleanup(); exit(1);} #else #define assert(x) #endif SHAR_EOF cat << \SHAR_EOF > cleanup.c /* cleanup - Hackercorp Hackercore, standard cleanup management routines, v1.2 This code is released to the public domain, 5/9/88, by Peter da Silva and Karl Lehenbauer Usage: add_cleanup(cleanup_routine); void cleanup_routine(); Add a routine to the cleanup list. When cleanup is called, routines passed as arguments to add_cleanup will be executed in the reverse order in which they were received. See the source to the IFF CAT archiver, iffar, for an example of how to use this. void cleanup() Execute all the routines passed as arguments to add_cleanup panic(s) char *s; Abort the program by printing a panic message including string "s" on stderr, flushing stdout and stderr, calling cleanup and exiting. _abort() By defining _abort to call panic, entering control-C while using stdio will cause the program to abort, executing your cleanup routines. Also note that Manx sdb calls _abort when the user requests an exit, so your cleanup routines will be executed then as well. */ #include #include struct _clean { int (*function)(); struct _clean *next; } *cleanlist = NULL; /* add_cleanup * given a function, add it to a list of functions to call when cleanup * is executed */ add_cleanup(function) int (*function)(); { struct _clean *ptr; ptr = AllocMem(sizeof(struct _clean), MEMF_PUBLIC); if(!ptr) return 0; ptr->function = function; ptr->next = cleanlist; cleanlist = ptr; } /* cleanup * call all the functions that were passed as arguments to add_cleanup * this run */ cleanup() { struct _clean *ptr; int (*f)(); while(cleanlist) { /* locate the next cleanup function and get the function pointer */ ptr = cleanlist; cleanlist = cleanlist->next; f = ptr->function; /* cleanup must clean up after itself */ FreeMem(ptr, sizeof(struct _clean)); /* execute the function */ (*f)(); } } /* panic - abort with an error message */ short panic_in_progress = 0; panic(s) char *s; { fflush(stdout); fprintf(stderr,"panic: %s\n",s); fflush(stderr); if (!panic_in_progress) { cleanup(); exit(10); } fprintf(stderr,"double panic!\n"); exit(11); } _abort() { panic("^C or other C library abort"); } SHAR_EOF cat << \SHAR_EOF > create.c /* iffar - IFF CAT archiver output and file copy rouines By Karl Lehenbauer, version 1.2, release date 5/9/88. This code is released to the public domain. See the README file for more information. */ /* culled from general purpose IFF file cracking routines for Karl's * Audio Stuff by Karl Lehenbauer, based originally on public domain IFF * code from Electronic Arts, 2/24/88 */ #include #include #include #include #include "assert.h" #include "iff.h" extern long lseek(); extern int verbose; extern char *basename(); #define ID_MISC MakeID('M','I','S','C') WriteCATheader(fd) int fd; { static ULONG dummy = ID_MISC; WriteChunk(fd,ID_CAT, &dummy, sizeof(dummy)); } /* write a chunk header, that's the 4-byte chunk ID and a 4-byte * chunk length */ WriteChunkHeader(fd,chunktype,length) int fd; ULONG chunktype; long length; { ChunkHeader chunkheader; chunkheader.ckID = chunktype; chunkheader.ckSize = length; if (write(fd,&chunkheader,sizeof(chunkheader)) == -1) { perror("WriteChunkHeader"); return(0); } return(1); } /* write a chunk that has a four character subtype, like FORM, CAT or LIST */ WriteSuperChunkHeader(fd,chunktype,subtype,length) int fd; ULONG chunktype, subtype; long length; { if (!WriteChunkHeader(fd,chunktype,length+sizeof(subtype))) return(0); return(write(fd,&subtype,sizeof(subtype)) != -1); } /* WriteCATentry This routine is given all of the info for a superchunk header and, in addition, a file name. It writes the chunk header and a FNAM chunk containing the file name out to the fd provided. */ WriteCATentry(fd,fname,chunktype,subtype,length) int fd; char *fname; ULONG chunktype, subtype; long length; { int fnamelen; int calc_chunk_length; /* Figure out the length of the file name. Remember that * it should be even. (I should use a cool macro to force * that, but I don't) * Add the size of the FNAM chunk we're going to write to our * calculated chunk length. */ fnamelen = strlen(fname); calc_chunk_length = fnamelen; if (fnamelen & 1) calc_chunk_length++; calc_chunk_length += length + sizeof(ChunkHeader); WriteSuperChunkHeader(fd,chunktype,subtype,calc_chunk_length); if (!WriteChunk(fd,ID_FNAM,fname,strlen(fname))) return(0); return(1); } /* write a chunk header and body, that's the 8-byte header and the data * to match the specified length */ WriteChunk(fd,chunktype,data,length) int fd; ULONG chunktype; char *data; long length; { static char zero = '\0'; if (!WriteChunkHeader(fd,chunktype,length)) return(0); /* if there's a body, write it out */ if (length != 0) if (write(fd,data,length) == -1) { perror("WriteChunk"); return(0); } /* if the length is odd, write out an additional zero byte */ if (length & 1) if (write(fd,&zero,1) == -1) { perror("WriteChunk"); return(0); } return(1); } /* checknew - given fname, this routine prints "Creating IFF CAT archive " * and the fname if file fname does not exist */ checknew(fname) char *fname; { extern int suppress_creation_message; int fd; if (!suppress_creation_message) { if ((fd = open(fname,O_RDONLY)) < 0) { fprintf(stderr,"Creating IFF CAT archive '%s'\n",fname); } else close(fd); } } /* open_quick_append * open the archive for append, verifying that it is IFF, subtype CAT, * that the length in the header matches the length of the file and * such. Note that this has been wrapped into a better OpenCAT routine * but I have not fixed open_quick_append to call it yet. */ int open_quick_append(fname) char *fname; { ChunkHeader mychunkheader; long filesize; int fd; /* If I can't open the archive read only, it doesn't exist, so * create it */ if ((fd = open(fname,O_RDONLY)) < 0) { if ((fd = create_archive(fname,ID_MISC)) < 0) { perror(fname); return(-1); } } else { /* read the IFF header and validate we've got a CAT */ if (read(fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader)) { perror(fname); fprintf(stderr,"couldn't read chunk header\n"); return(-1); } if (mychunkheader.ckID != ID_CAT) { fprintf(stderr,"file '%s' is not an IFF CAT archive\n",fname); return(-1); } /* verify that the header's filesize matches the file's size */ if ((filesize = lseek(fd,0,2)) == -1) { perror(fname); return(-1); } if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize) { fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",fname); fprintf(stderr,"I'm assuming it's OK and using file size.\n"); } /* ok, reopen the file for append and return the fd */ close(fd); if ((fd = open(fname,O_RDWR|O_APPEND)) < 0) { perror(fname); return(-1); } } return(fd); } #define COPY_BUFFER_SIZE 32768 char *copy_buffer = 0; /* append_file_to_archive * * this routine copies IFF file named by fname to the CAT archive known * by it's open-for-append fd. */ append_file_to_archive(fname,archive_fd) char *fname; int archive_fd; { char *basename_ptr; int bytes, i; int infd, fnamelen, basenamelen; ULONG chunkid, subtype; long chunksize, new_chunk_address; ULONG subchunkid; long subchunksize, placeholder, calculated_chunk_size, inputfilesize; /* seek to the end of the archive */ lseek(archive_fd,0,2); /* open the file to appended to the archive, read only */ if ((infd = open(fname,O_RDONLY)) == -1) { perror(fname); return(0); } /* get the filesize of the input file and relocate back to the start * of it */ inputfilesize = lseek(infd,0,2); lseek(infd,0,0); /* get the ID and size of the next chunk */ if ((chunkid = nextchunk(infd,&chunksize,&inputfilesize)) == 0) { fprintf(stderr,"couldn't get header chunk from file %s\n",fname); close(infd); return(0); } /* if the header isn't CAT, FORM or LIST, don't copy it */ if (chunkid != ID_CAT && chunkid != ID_FORM && chunkid != ID_LIST) { fprintf(stderr,"file %s is not an IFF CAT, FORM or LIST, ignored\n",fname); close(infd); return(0); } /* kludgily get the subtype - for FORMs, LISTs & CATs it's the * first 4 bytes of the chunk data. These are included in chunksize */ if (read(infd,&subtype,4) != 4) { perror("copy subtype"); return(0); } inputfilesize -= 4; /* record where we are in the archive so we can rewrite the header * which we'll need to do if we add a FNAM chunk */ new_chunk_address = lseek(archive_fd,0L,1) + 4; /* write in the chunk ID and the subtype - the program will * rewrite the length when we know for sure how long the * chunk is */ if (!WriteChunk(archive_fd,chunkid,&subtype,4)) { perror("append WriteChunk"); return(0); } /* keep track of the size of the FORM, CAT or LIST we're reading * through. We start with 4, the size of subtype written above */ calculated_chunk_size = 4; fnamelen = strlen(fname); /* if the filename includes a path, use only the base portion */ basename_ptr = basename(fname); basenamelen = strlen(basename); /* write a FNAM chunk, it's the basename determined above, * and our handle for it */ if (!WriteChunk(archive_fd,ID_FNAM,basename_ptr,basenamelen)) return(0); /* add size of the chunk header and the length of the chunk * body to the calculated chunk size. If the number is odd, * increment it by one as the IFF spec requires odd-sized * chunks to be one-null-padded to make them even. Note * that WriteChunk took care of it for the actual data written */ calculated_chunk_size += sizeof(ChunkHeader) + basenamelen; if (basenamelen & 1) calculated_chunk_size++; /* for all remaining chunks inside the FORM, LIST or CAT that * we're adding to the archive, */ while ((subchunkid = nextchunk(infd,&subchunksize,&inputfilesize)) != 0) { /* if it's an FNAM chunk, discard it */ if (subchunkid == ID_FNAM) skipchunk(infd,subchunksize,&inputfilesize); else { calculated_chunk_size += subchunksize + sizeof(ChunkHeader); if (subchunksize & 1) calculated_chunk_size++; /* write the chunk header for the embedded chunk we're copying */ if (!WriteChunkHeader(archive_fd,subchunkid,subchunksize)) return(0); /* now copy the embedded chunk's data */ copychunkbytes(infd,archive_fd,subchunksize,&inputfilesize); } } /* get current position in the archive, seek back to the header of the * FORM, CAT or LIST we just copied (into the archive) and write in the * correct chunk size, then seek back to where we were */ placeholder = lseek(archive_fd,0L,1); lseek(archive_fd,new_chunk_address,0); if (write(archive_fd,&calculated_chunk_size,4) != 4) { perror("archive subheader rewrite"); fprintf(stderr,"archive is blown.\n"); close(infd); return(0); } /* return to previous place in archive, close file we copied and * return 'success' */ lseek(archive_fd,placeholder,0); close(infd); return(1); } /* rewrite_archive_header - write (filesize - sizeof(ChunkHeader)) into * CAT archive's header, assumes file is open for write */ rewrite_archive_header(fd) { long filesize; /* get filesize by seeking to EOF */ if ((filesize = lseek(fd,0,2)) == -1) { perror("archive"); return(0); } /* go back to the beginning of the file */ if (lseek(fd,0,0) == -1) { perror("archive cleanup seek"); return(0); } if (!WriteChunkHeader(fd,ID_CAT,(filesize - sizeof(ChunkHeader)))) { perror("archive cleanup"); return(0); } return(1); } /* the copy buffer cleanup routine, it frees the copy buffer memory if * the buffer has been allocated */ void cleanup_copy_buffer() { if (copy_buffer) FreeMem(copy_buffer,COPY_BUFFER_SIZE); } /* copychunkbytes * * copy nbytes from infd to outfd, subtracting that amount from * the number of filebytes left within the virtual chunk, as given * by the address of that variable */ copychunkbytes(infd,outfd,nbytes,filebytes_left_ptr) int infd, outfd; long nbytes, *filebytes_left_ptr; { int copysize, odd; /* if we haven't allocated copy_buffer, allocate it and add it's cleanup * routine (cleanup_copy_buffer) to the cleanup list */ if (!copy_buffer) { if ((copy_buffer = (char *)AllocMem(COPY_BUFFER_SIZE,MEMF_FAST)) == (char *)NULL) panic("couldn't allocate copy buffer"); add_cleanup(cleanup_copy_buffer); } /* if nbytes of copying requested exceeds the virtual EOF (end of * the chunk's metachunk), truncate the chunk */ if (nbytes > *filebytes_left_ptr) { fprintf(stderr,"copychunkbytes: chunk size exceeds size of superchunk - truncating\n"); nbytes = *filebytes_left_ptr; } /* find out if the length of the chunk is odd or not - we'll need * it later to see if we need to write a pad byte */ odd = (nbytes & 1); /* do the copy, breaking it up into multiple COPY_BUFFER_SIZE sized * portions, if neccessary */ while (nbytes > 0) { copysize = (nbytes > COPY_BUFFER_SIZE) ? COPY_BUFFER_SIZE : nbytes; if (read(infd,copy_buffer,copysize) != copysize) { perror("copybytes input"); fprintf(stderr,"archive is blown.\n"); close(infd); return(0); } if (write(outfd,copy_buffer,copysize) != copysize) { perror("copybytes output"); fprintf(stderr,"archive is blown.\n"); close(infd); return(0); } /* update bytes left in chunk and in chunk's metachunk */ nbytes -= copysize; *filebytes_left_ptr -= copysize; } /* done with copy - if number of bytes copied was odd, read and * discard the pad byte, write out a pad byte and update the * virtual EOF (end of chunk) */ if (odd) { if (read(infd,copy_buffer,1) != 1) perror("copychunkbytes: failed to skip input byte"); assert(*copy_buffer == '\0'); write(outfd,copy_buffer,1); (*filebytes_left_ptr)--; } } /* create an archive by opening it, writing a CAT header, and returning the * file descriptor if it succeeded or -1 otherwise */ int create_archive(archive_name,subtype) char *archive_name; ULONG subtype; { int archive_fd; if ((archive_fd = open(archive_name, O_RDWR|O_CREAT|O_TRUNC)) == -1) { perror(archive_name); return(-1); } if (!WriteCATheader(archive_fd)) { fprintf("create_archive: couldn't write CAT chunkheader of new archive\n"); return(-1); } /* success, return the archive fd */ return(archive_fd); } /* end of create.c */ SHAR_EOF cat << \SHAR_EOF > delete.c /* iffar - IFF CAT archiver delete functions By Karl Lehenbauer, version 1.2, release date 5/9/88. This code is released to the public domain. See the README file for more information. */ #include #include #include #include #include "assert.h" #include "iff.h" extern ULONG nextchunk(); int delete_entries(archive_name,fnames,nfiles) char *archive_name; char *fnames[]; int nfiles; { int old_archive_fd, new_archive_fd; ULONG cat_type, chunkid, innerchunkid, subtype; long chunksize, innerchunksize, filesize; char textbuf[128], old_archive_name[128]; int i, delete_file, file_bytes; extern int verbose; /* rename the archive to its old name concatenated with ".old" */ sprintf(old_archive_name,"%s.old",archive_name); unlink(old_archive_name); rename(archive_name,old_archive_name); if ((old_archive_fd = OpenCAT(old_archive_name,&cat_type,&filesize)) == -1) { fprintf(stderr,"Can't open archive '%s'\n",old_archive_name); return(0); } if ((new_archive_fd = create_archive(archive_name,ID_MISC)) < 0) return(0); while ((chunkid = nextCATchunk(old_archive_fd,&subtype,&textbuf[0],&chunksize,&filesize)) != 0L) { /* if the chunk type isn't FORM, CAT or LIST, copy it across * without looking at it */ if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST) { if (!WriteChunkHeader(new_archive_fd,chunkid,chunksize)) return(0); copychunkbytes(old_archive_fd,new_archive_fd,chunksize,&filesize); } /* search to see if this chunk's name is one specified in fnames, * an array of pointer to char strings */ delete_file = 0; for (i = 0; i < nfiles; i++) { if (!strnicmp(fnames[i],textbuf, 128)) { delete_file = 1; break; } } /* if we did decide to delete it, skip it */ if (delete_file) { if (verbose) fprintf(stderr,"deleting %s\n",textbuf); if (!skipchunk(old_archive_fd,chunksize,&filesize)) { fprintf(stderr,"delete: skipchunk failed\n"); return(0); } } else /* we want to copy it */ { if (!WriteCATentry(new_archive_fd,textbuf,chunkid,subtype,chunksize)) return(0); copychunkbytes(old_archive_fd,new_archive_fd,chunksize,&filesize); } } /* write the right length in for the header */ rewrite_archive_header(new_archive_fd); /* close the old and new archive files and return success */ close(old_archive_fd); close(new_archive_fd); return(1); } /* end of extract.c */ SHAR_EOF cat << \SHAR_EOF > extract.c /* iffar - IFF CAT archiver extractor functions By Karl Lehenbauer, version 1.2, release date 5/9/88. This code is released to the public domain. See the README file for more information. */ #include #include #include #include #include "assert.h" #include "iff.h" #define ID_NAME MakeID('N','A','M','E') #define ID_AUTH MakeID('A','U','T','H') #define ID_ANNO MakeID('A','N','N','O') #define ID_Copyright MakeID('(','c',')',' ') extern long lseek(); extern ULONG nextchunk(); int extract(archive_name,fnames,nfiles) char *archive_name; char *fnames[]; int nfiles; { int archive_fd, outfd; ULONG cat_type, chunkid, innerchunkid, subtype; long chunksize, innerchunksize, filesize; char textbuf[256], *extract_name; long placeholder; int do_extract, i; extern int verbose; if ((archive_fd = OpenCAT(archive_name,&cat_type,&filesize)) == -1) { fprintf(stderr,"Can't open archive '%s'\n",archive_name); return(0); } while ((chunkid = nextCATchunk(archive_fd,&subtype,&textbuf[0],&chunksize,&filesize)) != 0L) { /* if the chunk type isn't FORM, CAT or LIST, skip it 'cuz it * isn't anything we know how to extract */ if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST) { if (!skipchunk(archive_fd,chunksize,&filesize)) { perror(archive_fd); fprintf(stderr,"extract: skipchunk failed\n"); return(0); } break; } /* if no extract names (nfiles == 0) were specified, extract all * files, so extract this one, else search to see if this name * is one specified in fnames, an array of pointers to char * strings */ do_extract = 0; if (nfiles == 0) { do_extract = 1; extract_name = textbuf; } else { for (i = 0; i < nfiles; i++) { if (!strnicmp(basename(fnames[i]),textbuf,128)) { do_extract = 1; extract_name = fnames[i]; break; } } } /* if we did decide to extract it, extract it */ if (do_extract) { if (verbose) fprintf(stderr,"extracting %s\n",extract_name); /* create, open and truncate the extract file */ if ((outfd = open(extract_name,O_RDWR|O_CREAT|O_TRUNC)) == -1) { perror(textbuf); } else { if (!WriteSuperChunkHeader(outfd,chunkid,subtype,chunksize)) return(0); copychunkbytes(archive_fd,outfd,chunksize,&filesize); close(outfd); } /* if they specified files on the command line, null them * out after we retrieve them, later we'll make a pass * through and complain about any we don't find with * their first bytes nulled out */ if (nfiles != 0) *fnames[i] = '\0'; } /* else we don't want to extract it, so skip it */ else if (!skipchunk(archive_fd,chunksize,&filesize)) { perror(archive_fd); fprintf(stderr,"extract: skipchunk failed\n"); return(0); } } /* complain about any files named that we didn't extract */ for (i = 0; i < nfiles; i++) { if (*fnames[i] != '\0') fprintf(stderr,"%s: no such archive entry\n",fnames[i]); } /* close the archive file and return success */ close(archive_fd); return(1); } /* end of extract.c */ SHAR_EOF # End of shell archive exit 0