Received: from PURCCVM.BITNET by FINTUVM.UTU.FI (Mailer X1.25) with BSMTP id
 6557; Sat, 10 Sep 88 07:53:16 EET
Received: from PURCCVM by PURCCVM.BITNET (Mailer X1.25) with BSMTP id 3765;
 Wed, 07 Sep 88 21:36:40 EST
Received: from j.cc.purdue.edu by VM.CC.PURDUE.EDU ; Wed, 07 Sep 88 21:36:32 EST
Received: by j.cc.purdue.edu (5.54/1.14)
    id AA11660; Wed, 7 Sep 88 21:35:32 EST
Date: Wed, 7 Sep 88 21:35:32 EST
From: ain@j.cc.purdue.edu (Patrick White)
Message-Id: <8809080235.AA11660@j.cc.purdue.edu>
To: fys-ma@fintuvm.BITNET
Subject: comp.sources.amiga:   sources/amiga/volume5/iffcat.s.sh1
 
Newsgroups: comp.sources.amiga
Subject: iffcat (sources 1 of 2)
Keywords: IFF, cat, tested
Approved: ain@j.cc.purdue.edu    (Patrick White)
 
Program Name:    iffcat  (sources 1 of 2)
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 <stdio.h>
#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 <exec/memory.h>
#include <stdio.h>
 
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 <exec/types.h>
#include <exec/memory.h>
#include <stdio.h>
#include <fcntl.h>
#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 <exec/types.h>
#include <exec/memory.h>
#include <stdio.h>
#include <fcntl.h>
#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 <exec/types.h>
#include <exec/memory.h>
#include <stdio.h>
#include <fcntl.h>
#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
