/* 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 <exec/types.h>
#include <exec/memory.h>
#include <stdio.h>
#include <fcntl.h>
#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 */

