Path: utzoo!attcan!uunet!wyse!vsi1!ames!husc6!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 2 of 2) Keywords: IFF, cat, tested Message-ID: <7728@j.cc.purdue.edu> Date: 8 Sep 88 14:35:19 GMT Organization: PUCC Land, USA Lines: 1299 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.sh2.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: # iff.c # iff.h # includes.c # main.c # misc.c # quickappend.c # replace.c # toc.c # This archive created: Tue Sep 6 13:38:53 1988 # By: Patrick White (PUCC Land, USA) cat << \SHAR_EOF > iff.c /* iffar - IFF CAT archiver, IFF support 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. */ /* culled general purpose IFF file cracking routines for Karl's * IFF Stuff by Karl Lehenbauer, based originally on public domain IFF * code from Electronic Arts, 2/24/88 */ #include #include #include #include #include #include "assert.h" #include "iff.h" extern long lseek(); extern ULONG nextchunk(); /* print a chunkID to stderr */ PutID(id) ID id; { fprintf(stderr,"%c%c%c%c", (char)((id>>24L) & 0x7f), (char)((id>>16L) & 0x7f), (char)((id>>8) & 0x7f), (char)(id & 0x7f) ); } UBYTE *MyAllocMem(bytes, type) ULONG bytes, type; { UBYTE *tmp; UBYTE *AllocMem(); tmp = AllocMem(bytes, type); return tmp; } /* return chunktype of next chunk */ /* every time nextchunk is executed and returns that it found a chunk, * either readchunk or skipchunk must be called and only one time! */ ULONG nextchunk(fd,chunksize,chunk_bytes_left) int fd; long *chunksize, *chunk_bytes_left; { int sawsize, i, blown = 0; ChunkHeader mychunkheader; char checkchar; /* if chunk_bytes_left is zero, we obey it as a virtual EOF, so * return 0 */ if (*chunk_bytes_left == 0) return(0); /* read the next chunk header */ if ((sawsize = read(fd,&mychunkheader,sizeof(mychunkheader))) != sizeof(mychunkheader)) { if (sawsize != 0) fprintf(stderr,"Something's wrong with nextchunk! (sawsize %d)\n", sawsize); *chunksize = 0; return(0); } #ifdef MAJORDEBUG fputs("nextchunk: next chunk '",stderr); PutID(mychunkheader.ckID); fprintf(stderr,"', size %d, parent bytes left %d\n",mychunkheader.ckSize,*chunk_bytes_left); #endif *chunksize = mychunkheader.ckSize; /* see if chunk ID looks OK */ for (i = 0; i < 4; i++) { checkchar = (mychunkheader.ckID >> (i * 8)) & 0xff; if (!isprint(checkchar)) { if (!blown) { blown = 1; fprintf(stderr,"nextchunk: chunk ID contains an unprintable character (0x%x)\n",checkchar); } break; } } /* see if chunk length is reasonable */ if ((mychunkheader.ckSize < 0) || (mychunkheader.ckSize > MAXCHUNKSIZE)) { fprintf(stderr,"nextchunk: chunk length of %ld is unreasonable\n",mychunkheader.ckSize); blown = 1; } if (blown) { fprintf(stderr,"nextchunk: I either got lost or the archive is blown\n"); return(0); } /* square up the bytes left in the chunk by the size of a chunk header, * eight bytes. We leave it to the caller to subtract the size of the * body of the chunk by calling skipchunk or readchunk */ *chunk_bytes_left -= sizeof(mychunkheader); if (*chunk_bytes_left < 0) { fprintf("nextchunk: chunk overran its parent by %d bytes\n",(0-*chunk_bytes_left)); *chunksize = 0; *chunk_bytes_left = 0; return(0); } return(mychunkheader.ckID); } /* read next chunk into buffer supplied, size must be value returned by * nextchunk * zero is returned on failure, one on success */ readchunk(fd,buf,size,chunk_bytes_left) int fd; char *buf; LONG size, *chunk_bytes_left; { *chunk_bytes_left -= size; if (*chunk_bytes_left < 0) { fprintf(stderr,"readchunk: chunk requested passed the end of its parent chunk\n"); *chunk_bytes_left = 0; return(0); } if (read(fd,buf,size) != size) { perror("smus file"); fputs("LoadSMUS: read of IFF chunk failed\n",stderr); return(0); } /* odd-length chunks have a trailer byte - skip it */ if (size & 1) { lseek(fd,1L,1); (*chunk_bytes_left)--; } return(1); } /* skip non-header portion of chunk, chunksize must have been returned * by nextchunk * returns 1 on success, 0 on failure */ skipchunk(fd,chunksize,chunk_bytes_left) int fd; LONG chunksize, *chunk_bytes_left; { *chunk_bytes_left -= chunksize; if (chunksize & 1) (*chunk_bytes_left)--; if (*chunk_bytes_left < 0) { fprintf(stderr,"skipchunk: chunk size passes end of parent chunk's data by %d bytes\n",0 - *chunk_bytes_left); return(0); } /* skip over chunk data and skip an extra byte if length is odd */ lseek(fd,(long)chunksize,1); if (chunksize & 1) lseek(fd,1L,1); return(1); } /* OpenIFF * given file name, open the IFF file. * read the header, return failure if it's not a FORM * (someday we'll handle the more complex types) * read the form type, return failure if it's not the type requested * success, return the file descriptor */ int OpenIFF(fname,expected_formtype,length_ptr) char *fname; LONG expected_formtype; LONG *length_ptr; { int iffile; ChunkHeader chunkhead; LONG formtype; /* open the file */ if ((iffile = open(fname, O_RDONLY)) < 0) { fprintf(stderr,"OpenIFF: can't open IFF SMUS file %s\n",fname); perror(fname); return(-1); } /* get the length */ *length_ptr = lseek(iffile,0,2); lseek(iffile,0,0); /* read the header chunk */ if (read(iffile, &chunkhead, sizeof(chunkhead)) < 0) { fprintf(stderr,"OpenIFF: initial read from IFF file %s failed!\n",fname); return(-1); } /* return if the header chunk doesn't say it's IFF FORM */ if (chunkhead.ckID != ID_FORM) { fprintf(stderr,"OpenIFF: File %s isn't IFF, is too complex, or doesn't start with FORM\n",fname); return(-1); } /* fprintf(stderr,"OpenIFF: FORM found, size is %d\n",chunkhead.ckSize); */ /* read the form type */ read(iffile, &formtype, sizeof(formtype)); /* return if the form type isn't the type requested */ if (formtype != expected_formtype) { fprintf(stderr,"OpenIFF: File %s is IFF "); PutID(formtype); fprintf(stderr," rather than the requested "); PutID(expected_formtype); fprintf(stderr,"\n"); return(-1); } return(iffile); } /* read chunks until one of type chunktype is found or EOF * note that after a successful call to chunkuntil, * skipchunk or readchunk must be performed or the IFF reading * software will get lost on the next nextchunk * chunksize is returned on success, -1 otherwise * The caller should probably check the return explicitly for -1. * If checking only for less than zero, chunks larger than * two gigabytes will cause your code to break. */ LONG chunkuntil(fd,chunktype,file_bytes_left) int fd; ULONG chunktype; long *file_bytes_left; { ULONG currentchunk; LONG chunksize; while ((currentchunk = nextchunk(fd,&chunksize,file_bytes_left)) != NULL) { if (currentchunk == chunktype) return(chunksize); skipchunk(fd,chunksize,file_bytes_left); } return(0); } /* OpenCAT - Open an IFF CAT archive */ /* OpenCAT * Open an IFF CAT archive, insuring that the file starts with an * IFF CAT header and that the length in the header is valid. * Return the CAT subtype, file descriptor and length, leaving the * file pointed at the start of the first subchunk */ int OpenCAT(archive_name,subtype_ptr,length_ptr) char *archive_name; ULONG *subtype_ptr, *length_ptr; { ChunkHeader mychunkheader; int archive_fd; long start_of_body, filesize; long placeholder; if ((archive_fd = open(archive_name,O_RDONLY)) == -1) { /* fprintf(stderr,"Can't open archive '%s'\n",archive_name); */ return(-1); } if (read(archive_fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader)) { perror(archive_name); 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",archive_name); return(-1); } if (read(archive_fd,subtype_ptr,sizeof(subtype_ptr)) != sizeof(subtype_ptr)) { fprintf(stderr,"error reading archive header - subtype\n"); return(-1); } /* save location of current start of body */ if ((start_of_body = lseek(archive_fd,0,1)) == -1) { perror(archive_name); return(-1); } /* seek to the end to get the size */ if ((filesize = lseek(archive_fd,0,2)) == -1) { perror(archive_name); return(-1); } /* see if the shoe fits */ if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize) { fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",archive_name); fprintf(stderr,"I'm assuming it's blown.\n"); return(-1); } /* go back to the start of the IFF CAT archive's data */ if (lseek(archive_fd,start_of_body,0) == -1) { perror(archive_name); return(-1); } /* it worked store filesize in location pointed to by 'length' * and return the archive file's file descriptor */ *length_ptr = filesize; return(archive_fd); } /* end of OpenCAT */ /* nextcat - read header info for the next entry in an IFF CAT */ /* nextCATchunk * * given fd, read into IFF file. * if we're not at a FORM, CAT or LIST, print the chunk type if verbose, * then skip the chunk * if we are at a FORM, CAT or LIST, read the subtype and return it * via the argument subtype_ptr. * if the next chunk within the embedded FORM, CAT or LIST is FNAM, * read the text in the FNAM chunk (file name) and write it into space * pointed to by argument fname_ptr. * return the size of the chunk in argument chunk_length_ptr. * update the space left in the metachunk (usually the file) of argument * metachunk_length_ptr */ ULONG nextCATchunk(fd,subtype_ptr,fname_ptr,chunk_length_ptr,metachunk_length_ptr) int fd; ULONG *subtype_ptr; char *fname_ptr; LONG *chunk_length_ptr, *metachunk_length_ptr; { ULONG cat_type, chunkid, innerchunkid; long chunksize, innerchunkposition, innerchunksize, filesize; int odd; /* null out the returned subtype and fnam */ *subtype_ptr = 0L; *fname_ptr = '\0'; if ((chunkid = nextchunk(fd,chunk_length_ptr,metachunk_length_ptr)) == 0L) return(0L); /* if the chunk type isn't FORM, CAT or LIST, return the chunkid */ if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST) return(chunkid); /* get the chunk subtype */ if (read(fd,subtype_ptr,4) != 4) { perror("reading subtype"); return(0); } /* reduce chunksize and metachunksize by the size of the subtype */ *chunk_length_ptr -= sizeof(ULONG); *metachunk_length_ptr -= sizeof(ULONG); /* sneak a peek into the embedded FORM, CAT or LIST to see * if the next chunk is an FNAM chunk */ assert(*chunk_length_ptr > 0); /* fetch the current location in the file - we'll restore it * if we don't find this next chunk to be a FNAM one */ innerchunkposition = lseek(fd,0L,1); /* get the type and size of the inner chunk */ chunksize = *chunk_length_ptr; innerchunkid = nextchunk(fd,&innerchunksize,&chunksize); /* if it's not an fname chunk, seek back to the start of the * chunk and return the chunk id - master length should be OK */ if (innerchunkid != ID_FNAM) { lseek(fd,innerchunkposition,0); return(chunkid); } odd = innerchunksize & 1; /* read and zero-terminate the file name (contents of FNAM chunk) */ if (!readchunk(fd,fname_ptr,innerchunksize,&chunksize)) { fprintf(stderr,"nextCATchunk: got into trouble reading chunk text\n"); return(0); } *(fname_ptr + innerchunksize) = '\0'; /* update the length of the chunk and its parent & return the chunk id * (nextchunk normally handles updating the length but we used different * variables to make restoring (in case we don't find an FNAM chunk) * easier */ *chunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize); *metachunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize); if (odd) { (*chunk_length_ptr)--; (*metachunk_length_ptr)--; } return(chunkid); } /* end of nextCATchunk */ /* end of iff.c */ SHAR_EOF cat << \SHAR_EOF > iff.h #ifndef IFF_H #define IFF_H /* IFF.H defs for IFF-85 Interchange Format Files. 1/22/86 By Jerry Morrison and Steve Shaw, Electronic Arts. Mods and stripping of a lot of stuff by Karl Lehenbauer, 1987, 1988 This software is in the public domain. */ #ifndef LIBRARIES_DOS_H #include "libraries/dos.h" #endif typedef LONG ID; /* Four-character IDentifier builder.*/ #define MakeID(a,b,c,d) ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) ) /* Standard group IDs. A chunk with one of these IDs contains a SubTypeID followed by zero or more chunks.*/ #define ID_FORM MakeID('F','O','R','M') #define ID_PROP MakeID('P','R','O','P') #define ID_LIST MakeID('L','I','S','T') #define ID_CAT MakeID('C','A','T',' ') #define ID_FILLER MakeID(' ',' ',' ',' ') #define ID_FNAM MakeID('F','N','A','M') #define ID_MISC MakeID('M','I','S','C') /* The IDs "FOR1".."FOR9", "LIS1".."LIS9", & "CAT1".."CAT9" are reserved * for future standardization.*/ /* ---------- Chunk ----------------------------------------------------*/ /* All chunks start with a type ID and a count of the data bytes that follow--the chunk's "logicl size" or "data size". If that number is odd, a 0 pad byte is written, too. */ typedef struct { ID ckID; LONG ckSize; } ChunkHeader; typedef struct { ID ckID; LONG ckSize; UBYTE ckData[ 1 /*REALLY: ckSize*/ ]; } Chunk; /* define the maximum reasonable chunk size - the real max is around 2^32, * but we need some reasonability limit to check for */ #define MAXCHUNKSIZE 900000 /* The Grouping chunks (LIST, FORM, PROP, & CAT) contain concatenations of * chunks after a subtype ID that identifies the content chunks. * "FORM type XXXX", "LIST of FORM type XXXX", "PROPerties associated * with FORM type XXXX", or "conCATenation of XXXX".*/ typedef struct { ID ckID; LONG ckSize; /* this ckSize includes "grpSubID".*/ ID grpSubID; } GroupHeader; typedef struct { ID ckID; LONG ckSize; ID grpSubID; UBYTE grpData[ 1 /*REALLY: ckSize-sizeof(grpSubID)*/ ]; } GroupChunk; #endif IFF_H SHAR_EOF cat << \SHAR_EOF > includes.c /* iffar - archiver includes.c - for Manx +H +I options to precompile includes */ /* iffar - IFF CAT archiver includes.c for include precompile 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 */ #include #include #include "assert.h" #include "iff.h" /* end of includes.c */ SHAR_EOF cat << \SHAR_EOF > main.c /* iffar - IFF CAT archiver, main() 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. */ /* iffar - iff 'cat' archiver, 3/26/88 */ #include #include "assert.h" /* options */ int verbose = 0; int insert_after = 0; int insert_before = 0; int suppress_creation_message = 0; /* if insert_after or insert_before is set, this global has the relevant * name */ char *location_modifier_name; /* variables */ int number_of_arguments; /* number of args to the program, a global */ /* this'll point to the command line's command char string */ char *cmdkeys; /* this'll be the name of the archive file */ char *archive_name; /* this'll contain the command */ char command_key; /* if a command must have some names as arguments, must_have_names can be * called to insure that arguments were supplied. * Note that what constitutes the presence of arguments varies whether * a positional modifier ('i', 'b' or 'a') was specified. */ must_have_names() { if ((insert_before || insert_after) && (number_of_arguments < 5)) { fprintf(stderr,"a positioning element and at least one other element must be specified for\n"); fprintf(stderr,"the combination of options you have requested.\n"); usage(); } if (number_of_arguments < 4) { fprintf(stderr,"at least one archive element must be specified for this option\n"); usage(); } } header() { fprintf(stderr,"iffar - public domain IFF CAT archiver, Version 1.1, by Karl Lehenbauer\n\n"); fprintf(stderr,"This program maintains archives of IFF FORM, CAT and LIST files\n"); fprintf(stderr,"in a manner that complies with the IFF CAT specification.\n"); } /* usage print usage text and exit */ usage() { fprintf(stderr,"\nUsage: iffar key [posname] afile name ...\n\n"); fprintf(stderr,"key can be one of the following:\n"); fprintf(stderr,"\td\tdelete\n\tr\treplace\n\tq\tquick append\n"); fprintf(stderr,"\tt\ttable of contents\n\tx\textract\n"); fprintf(stderr,"and zero or more of the following options:\n"); fprintf(stderr,"\tv\tverbose\n\ta\tafter\n\ti,b\tbefore\n"); fprintf(stderr,"\tc\tsuppress creation message\n"); cleanup(); exit(1); } main(argc,argv) int argc; char *argv[]; { int i, j; int archive_fd, files; char *nameptr; char **entryname_pointers; int entrycount; number_of_arguments = argc; if (argc < 3) { header(); usage(); } /* assign nice variable names to various command line arguments */ cmdkeys = argv[1]; command_key = *cmdkeys; archive_name = argv[2]; location_modifier_name = (char *)NULL; entryname_pointers = &argv[3]; entrycount = argc - 3; /* figure out any modifiers specified to the main function requested */ for (i = 1; i < strlen(cmdkeys); i++) { switch(cmdkeys[i]) { case 'v': /* verbose selected, set verbose flag */ verbose = 1; break; /* 'after' option, make sure they've selected 'r' or 'm' as * the main command. Also, make sure they haven't already * chosen the insert_before option. After that, we're OK, * so twiddle parameters and break */ case 'a': if (command_key != 'r' && command_key != 'm') { fprintf(stderr,"i, b and a modifiers are only good for r and m options\n"); usage(); } if (insert_before) { fprintf(stderr,"you can't select 'insert before' and 'insert after'\n"); usage(); } insert_after = 1; location_modifier_name = argv[3]; entryname_pointers = &argv[4]; entrycount = argc - 4; break; /* 'insert before' option, make sure they've selected 'r' or * 'm' as the main command. Also, make sure they haven't already * chosen the insert_after option. After that, we're OK, * so twiddle parameters and break */ case 'i': /* insert; before */ case 'b': if (command_key != 'r' && command_key != 'm') { fprintf(stderr,"i, b and a modifiers are only good for r and m options\n"); usage(); } if (insert_after) { fprintf(stderr,"you can't select 'insert before' and 'insert after'\n"); usage(); } insert_before = 1; location_modifier_name = argv[3]; entryname_pointers = &argv[4]; entrycount = argc - 4; break; case 'c': /* supress creation message */ suppress_creation_message = 1; break; default: /* unrecognized option */ fprintf(stderr,"option '%c' unrecognized\n",cmdkeys[i]); usage(); } } /* now execute the major command */ switch(command_key) { case 'd': /* delete */ must_have_names(); delete_entries(archive_name,entryname_pointers,entrycount); break; /* replace - if the archive doesn't exist, fall through to * quick append */ case 'r': must_have_names(); if ((archive_fd = open(archive_name,O_RDONLY)) != -1) { close(archive_fd); replace_entries(archive_name,entryname_pointers,entrycount); break; } case 'q': /* quick append */ must_have_names(); checknew(archive_name); if ((archive_fd = open_quick_append(archive_name)) == -1) break; quickappend_entries(archive_fd,entryname_pointers,entrycount); break; case 't': /* table of contents */ table_of_contents(archive_name); break; case 'm': /* move */ fprintf(stderr,"move option not implemented\n"); must_have_names(); break; case 'x': /* extract */ extract(archive_name,entryname_pointers,entrycount); break; default: fprintf(stderr,"requested command (%c) is invalid\n",command_key); usage(); } cleanup(); exit(0); } /* end of main.c */ SHAR_EOF cat << \SHAR_EOF > misc.c /* iffar - IFF CAT archiver miscellaneous 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 /* strnicmp - case-insensitive strncmp, not provided by manx, this one * is like lattice's */ char *strnicmp(s1, s2, len) char *s1, *s2; int len; { char c1, c2; c1 = *s1; c2 = *s2; while (len-->0 && *s1) { if (isupper(*s1)) c1 = tolower(*s1); else c1 = *s1; s1++; if (isupper(*s2)) c2 = tolower(*s2); else c2 = *s2; s2++; if (c1 != c2) break; } return c1 - c2; } /* return the base portion of a file name - needs to be hacked in C style BWTF*/ char *basename(fname) char *fname; { char *basename_ptr; int i; int fnamelen = strlen(fname); basename_ptr = fname; for (i = fnamelen - 1; i > 0; i--) { if (fname[i] == '/' || fname[i] == ':') { basename_ptr = &fname[i+1]; break; } } return(basename_ptr); } /* end of misc.c */ SHAR_EOF cat << \SHAR_EOF > quickappend.c /* iffar - IFF CAT archiver quick append 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 "assert.h" extern int verbose; /* append a list of files specified by an array of pointers to * names, the number of entries in the array, and a file decscriptor * to write them to */ quickappend_entries(archive_fd,entryname_pointers,entrycount) int archive_fd; char *entryname_pointers[]; int entrycount; { int files; char *nameptr; /* for all names specified on the command line */ for (files = 0; files < entrycount; files++) { /* scdir will return nonnull values as long as the pattern * matches files (it will return patternless names the first * time and NULL the second */ while (nameptr = scdir(entryname_pointers[files])) { /* append the file */ if (append_file_to_archive(nameptr,archive_fd)) { if (verbose) fprintf(stderr,"appended %s\n",nameptr); } } } if (!rewrite_archive_header(archive_fd)) { fprintf(stderr,"rewrite of archive header failed... archive is presumed blown\n"); } close(archive_fd); } /* end of quickappend.c */ SHAR_EOF cat << \SHAR_EOF > replace.c /* iffar - IFF CAT archiver replace 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" /* if a "insert before" or "insert after" position modifier is * not specified, the program attepts to replace entries at the * same place as where they occurred in the original archive. * if the entry could not be found, the file is appended to the * end of the archive. hence, when a position modifier is not * specified and a file being replaced did not exist in the archive, * the file needs to be appended onto the * end - we do this by nulling out the argument if and only if * neiter insert_before or insert_after is set, else a bug would * be introduced as when they are selected the delete and insert * portions are asynchronous and either could occur before the * other, hence one guy nulling it makes the other guy not see * it. it's not a problem for the specified case, though, 'cuz * it's only looked at in this one place and at the end, * where, again only when neither insert_before and insert_after * are checked, we run through the arguments and if the first byte * of any entry is not null, this implies that entry was not in * the original archive, hence not replaced in-place, so we tack * it on to the end. * if insert_before or insert_after is selected, archive entries * are deleted on the fly as they are read from the original * archive by being skipped and, when the insert conditions are * met, all of the names in the list of names are appended, thus, * we don't have to worry about it * * note that it's kind of broken for insert_after and insert_before * when the after or before name isn't found the old entries will * be deleted but the new ones won't be inserted, heck, the program * may even blow up */ extern ULONG nextchunk(); extern int insert_before; extern int insert_after; extern char *location_modifier_name; int replace_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, replace_file, file_bytes; int entryindex, insert_next_time = 0; int modifier_matches, did_insert = 0; 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); break; } /* we shouldn't ever not get a filename back here at the top level * that is, we saw a CAT, LIST or FORM, we expect a FNAM chunk*/ if (textbuf[0] == '\0') { fprintf(stderr,"FORM, CAT or LIST in archive doesn't have an FNAM chunk, abandoning\n"); return(0); } if (insert_before || insert_after) modifier_matches = !strnicmp(location_modifier_name,textbuf,128); /* if insert_before option has been selected and the chunk ID * we just read from the old archive matches global * location_modifier_name, append all the named files to the * new archive. also do this if it's insert_after and * we matched the name last time */ if ((modifier_matches && insert_before) || insert_next_time) { insert_next_time = 0; for (entryindex = 0; entryindex < nfiles; entryindex++) { if (verbose) { if (insert_before) fprintf(stderr,"inserting "); else fprintf(stderr,"appending "); fprintf(stderr,"%s\n",fnames[entryindex]); } append_file_to_archive(fnames[entryindex],new_archive_fd); } did_insert = 1; } /* if this is a match and insert_after is selected, set * insert_next_time so we'll know to do the appends after the * next entry */ if (modifier_matches && insert_after) insert_next_time = 1; /* search to see if this chunk's name is one specified in fnames, * an array of pointer to char strings */ replace_file = 0; for (i = 0; i < nfiles; i++) { if (!strnicmp(basename(fnames[i]),textbuf,128)) { /* it is */ replace_file = 1; break; } } /* if we want to replace it, */ if (replace_file) { /* copy the file being replaced into the archive if * neither insert_before or insert_after are set. * if they are set, this will be done elsewhere */ if (!insert_before && !insert_after) { if (verbose) fprintf(stderr,"replacing %s\n",textbuf); append_file_to_archive(fnames[i],new_archive_fd); /* null out the first byte of the name so we won't * append it again at the end */ *fnames[i] = '\0'; } else if (verbose) fprintf(stderr,"removing old %s\n",textbuf); /* in either case (replace selected, we don't care if * before or after are chosen), skip the chunk in the * old archive */ if (!skipchunk(old_archive_fd,chunksize,&filesize)) { fprintf(stderr,"replace: skipchunk failed\n"); return(0); } } else /* not on the replacement list, we copy it */ { if (!WriteCATentry(new_archive_fd,textbuf,chunkid,subtype,chunksize)) return(0); copychunkbytes(old_archive_fd,new_archive_fd,chunksize,&filesize); } } /* now if insert_before or insert_after were not set, for all entries * in the list that don't have a null byte for their first byte, * append them to the archive */ if ((!insert_before && !insert_after) || !did_insert) { /* if we didn't do the insert, report that we're planning * to append. note that if insert_after and !insert_next_time * it means that they said append after the last one, so * don't mention it */ if (insert_before || (insert_after && !insert_next_time)) fprintf(stderr,"couldn't find entry %s that was specified as a position modifier\n, appending your entries\n",textbuf); for (i = 0; i < nfiles; i++) { if (*fnames[i] != '\0') { if (verbose) fprintf(stderr,"appending %s\n",fnames[i]); append_file_to_archive(fnames[i],new_archive_fd); } } } /* 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 > toc.c /* iffar - IFF CAT archiver table of contents 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 table_of_contents(fname) char *fname; { int fd; ULONG cat_type, chunkid, innerchunkid, subtype; long chunksize, innerchunksize, filesize; char namebuf[256]; extern int verbose; if ((fd = OpenCAT(fname,&cat_type,&filesize)) == -1) { fprintf(stderr,"Can't open archive '%s'\n",fname); return(0); } if (verbose) { fprintf(stderr,"CAT subtype is "); PutID(cat_type); fprintf(stderr,"\n"); } while ((chunkid = nextCATchunk(fd,&subtype,&namebuf[0],&chunksize,&filesize)) != 0L) { /* if the chunk type isn't FORM, CAT or LIST, skip it */ if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST) { if (verbose) { PutID(chunkid); fprintf(stderr," chunk is being skipped\n"); } skipchunk(fd,chunksize,&filesize); goto bigchunkdone; } if (namebuf[0] == '\0') { fprintf(stderr,"CAT entry didn't contain a FNAM chunk - skipping\n"); skipchunk(fd,chunksize,&filesize); goto bigchunkdone; } PutID(chunkid); fprintf(stderr," "); /* print out the chunk length */ PutID(subtype); fprintf(stderr," %6ld %s\n",chunksize, namebuf); /* if verbose isn't selected, skip the rest of the chunks in the * embedded FORM, CAT or LIST */ if (!verbose) { skipchunk(fd,chunksize,&filesize); goto bigchunkdone; } /* else verbose is selected, dive down into the embedded FORM, * CAT or LIST and print chunks found there and their contents * when they're known to be text. */ /* follow a good neighbor policy by reducing our (the parent's) * chunk size by the size we've found - since we'll be moving * through the embedded FORM effectively recursively; that is, * we'll be using the same nextchunk, skipchunk, readchunk * routines on the chunk nextchunk got for us, we'll * decrease parent chunk size by the size we've found, * as, when calling nextchunk, skipchunk and readchunk below, * we'll be concerned with keeping track of the form's chunk length * as a virtual EOF, rather than the whole CAT's as above */ filesize -= chunksize; if (chunksize & 1) filesize--; while (chunksize != 0) { /* get the type and size of the inner chunk */ innerchunkid = nextchunk(fd,&innerchunksize,&chunksize); if (chunksize == 0) break; /* print some presumably useful information */ fprintf(stderr," "); PutID(innerchunkid); switch(innerchunkid) { case ID_FNAM: panic("more than one FNAM chunk in an embedded FORM"); case ID_NAME: case ID_AUTH: case ID_ANNO: case ID_Copyright: if (innerchunksize >= sizeof(namebuf)) { fprintf(stderr,"[too long]\n"); skipchunk(fd,innerchunksize,&chunksize); } else { if (!readchunk(fd,namebuf,innerchunksize,&chunksize)) { fprintf(stderr,"got into trouble reading chunk text\n"); return(0); } namebuf[innerchunksize] = '\0'; fprintf(stderr,", %s\n",namebuf); } break; default: fprintf(stderr,", size %ld\n",innerchunksize); /* skip the body of the subchunk */ skipchunk(fd,innerchunksize,&chunksize); } } /* we make it to here if the length of all the subchunks equalled * the length of their superchunk */ bigchunkdone: ; } close(fd); } /* end of toc.c */ SHAR_EOF # End of shell archive exit 0