/* Import .o and .a (a.out format) files into a cff archive */
/* Copyright 1993 Norman D. Culver, All Rights Reserved */

#include <stdlib.h>
#include <time.h>
#include "cff.h"

/* Program version */
static Item prgversion = {1};
/* Archive version */
static Item arversion;

/* Maximum library types */
#define MAXLIBTYPE 16

typedef struct {
	unsigned long sn;
	unsigned long libnum : 12;	/* capacity for 4096 libraries per libtype */
	unsigned long libtype : 4;	/* capacity for 16 library types */
	unsigned long unused : 12;
	unsigned long tag : 4;
} SymItem;

typedef struct {
	unsigned long filetime;
	unsigned long sn : 28;
	unsigned long tag : 4;
} OrgItem;

/* Action bitmap */
static int act = 0;
#define arADD (1)
#define arREPLACE (2)
#define arDELETE (4)
#define arEXTRACT (8)
#define arCHECK (16)
#define arUPDATE (32)

static int verbose = 0;		/* if 1, verbose mode */
static long libtype = 0;	/* 0 = modules, 1 = classes */
static long oldlibtype = -1;
static long libnum = 0;		/* library number into which files are stored */
static long oldlibnum = -1;
static int map = 0;			/* if 1, print archive map */
static int map_bitmaps = 0;	/* if 1, and map, print bitmaps */
static int map_entries = 0; /* if 1, and map, print entries */
static int freshen = 0;		/* if 1, freshen entries */
static int shrink = 0;		/* if 1, shrink archive */
static int issymbol = 0;	/* if 1, file names are really symbol names */
static int makefile = 0;	/* if 1, print a makefile */
static void *archive;		/* handle of the open archive */
static void *msyms;			/* handle of the master symbol table */
static void *ipaths;		/* handle of the internal path table */
static void *qipaths;
static void *origins;		/* handle of the origin path table */
static void *qorigins;
static void *lib;			/* handle of the current internal library */
static void *plib;			/* handle of the library parent directory */
static char curlib[64];		/* internal path to the current library */
static int curliblen;
static char curobj[256];	/* internal path to the current .o file */
static int curobjlen;
static char *cur_orgpath;	/* current origin path as given by cfpathtrn */
static int cur_orgtype;		/* current origin type as given by cfpathtrn */
static OrgItem cur_org;		/* current origin time and serial num */
static char ltypes[MAXLIBTYPE];
static char lnums[4096];
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
static int
open_curlib()
{
	if(lib)
	{/* library is already open */
		if(libtype == oldlibtype && libnum == oldlibnum)
			return 1;

		cfclrlazy(lib);
		cfclose(lib);
		if(oldlibtype != libtype) {
			cfclose(plib);
			cfsprintf(curlib, "ltype%d", libtype);
			plib = cfsubopen(archive, curlib, F_RDWR|F_CREAT, NULL);
		}
		oldlibtype = libtype;
		oldlibnum = libnum;
	}
	if(oldlibtype != libtype) {
		cfsprintf(curlib, "ltype%d", libtype);
		plib = cfsubopen(archive, curlib, F_RDWR|F_CREAT, NULL);
	}
	curliblen = cfsprintf(curlib, "ltype%d/lib%d", libtype, libnum);
	lib = cfsubopen(archive, curlib, F_RDWR|F_CREAT|F_SORTED, NULL);
	if(!lib) {
		cfprintf("cfar: can't open library %s\n", curlib);
		return 0;
	}
	cfsetlazy(lib);
	return 1;
}
static void *
make_scratch()
{
	return cfsubopen(MEMTEMP, "", F_TEMP|F_UNIQ|F_RDWR|F_CREAT|F_BITMAP, NULL);
}
static void
get_libtypes(char *name)
{
void *dirh;
CFDIRENT *d;
	mymemclr(ltypes, MAXLIBTYPE);
	dirh = cfopendir(name);
	while((d = cfreaddir(dirh)) != NULL) {
		if(		d->d_name[0] == 'l'
			&&	d->d_name[1] == 't')
		{
		long x;
			myatol(&d->d_name[5], &x);
			ltypes[x] = 1;
		}
	}
	cfclosedir(dirh);
}
static void
get_libnums(char *name)
{
void *dirh;
CFDIRENT *d;
	mymemclr(lnums, 4096);		
	dirh = cfopendir(name);
	while((d = cfreaddir(dirh)) != NULL)
	{
	long x;
		myatol(&d->d_name[3], &x);
		lnums[x] = 1;
	}
	cfclosedir(dirh);
}
static unsigned long
new_sn(char *name)
{
Item sn;
int namlen = mystrlen(name);
	if(cffind(archive, name, namlen, &sn) >= FOUND)
	{
		++sn.a0;
		cfreinsert(archive, name, namlen, &sn);
		return sn.a0;
	}
	return 0;
}
static char *
fileof(char *path)
{
int len = mystrlen(path);
char *cp = &path[len-1];
char *cp1;
	while(cp != path)
	{
		if(*cp == '/' || *cp == '\\' || *cp == ':') {
			++cp;
			break;
		}
		--cp;
	}
	len = mystrlen(cp);
	cp1 = malloc(len+1);
	mystrcpy(cp1, cp);
	return cp1;
}
char *
objof(char *name)
{
	if(issymbol) {
	long dupnum = 0;
	SymItem symval;
	int symlen = mystrlen(name);
		for(;;)
		{
		int	result = cffind_dupnum(msyms, name, symlen, &symval, &dupnum);

			if(result >= FOUND)
			{
				if(		(act == arCHECK)
					||	(	symval.libtype == libtype
						 &&	symval.libnum == libnum))
				{
				char curpath[256];
					if(cfget(qipaths, &symval, 8, curpath, 256) >= FOUND) {
					  if(act == arCHECK)
						cfprintf("symbol %s is defined in %s\n", name, curpath);
					  else return fileof(curpath);
					}
				}
				++dupnum;
			}
			else return NULL;
		}
	}
	else return fileof(name);
}
static void
delete_file(char *name, int internal)
{
char *objname = objof(name);
int objlen;
Item packval;
SymItem symval;
char cpath[256];
int cpathlen;
Item objloc;

	if(!objname)
		return;

	cpathlen = cfsprintf(cpath, "%s/%s", curlib, objname);

	/* Delete the .o file data chunk */

	objlen = mystrlen(objname);
	if(cffind(lib, objname, objlen, &objloc) < FOUND) {
		if(!internal)
			cfprintf("cfar: %s not found\n", cpath);
		return;
	}
	if(verbose && !internal)
		cfprintf("%s\n", objname);

	cfdelete(lib, objname, objlen);

	/* Read the quick access symbol pack and delete the symbols */
	objname[objlen-1] = 'x';
	if(cffind(lib, objname, objlen, &packval) >= FOUND)
	{
	char *cp, *sp, symlen;
		cp = cflocalize(lib, &packval);
		for(sp = cp; *sp ; sp += symlen+1)
		{
		long dupnum = 0;
			symlen = mystrlen(sp);			
			for(;;)
			{
			int	result = cffind_dupnum(msyms, sp, symlen, &symval, &dupnum);
				if(result >= FOUND)
				{
					if(		symval.libtype == libtype
						&&	symval.libnum == libnum)
					{
						cfdelete_dupnum(msyms, sp, symlen, dupnum);
						break;
					}
					++dupnum;
				}
				else break;
			}
		}
		cfrelease(cp, R_CLEAN);
		/* Delete the symbol pack */
		cfdelete(lib, objname, objlen);
		/* Delete the origin linkage */
		objname[objlen-1] = 't';
		cfdelete(lib, objname, objlen);
	}
	/* Delete the path info */
	cfdelete(ipaths, cpath, cpathlen);
	cfdelete(qipaths, &symval, 8);
	free(objname);
}
static void
extract_file(char *name)
{
char *objname = objof(name);
int objlen;
Item objloc;

	if(!objname)
		return;

	objlen = mystrlen(objname);
	if(cffind(lib, objname, objlen, &objloc) == FOUND)
	{
	void *hobj;
	void *fd;

		if((hobj = cfopen_chunk(lib, &objloc)) != NULL)
		{
			if((fd = cfopen(objname, F_RDWR|F_CREAT, NULL)) != NULL)
			{
				cfcopy_file(fd, hobj);
				cfclose(fd);
				if(verbose)
					cfprintf("%s\n", objname);
			}
			cfclose(hobj);
		}
	}
	free(objname);
}
static void
setup_origin(unsigned long filetime)
{
int orglen;
char *cp;
Item orgsn;

	cur_org.filetime = filetime;
	cp = &cur_orgpath[mystrlen(cur_orgpath)-1];
	if(*cp != 'a' || cp[-1] != '.')
	{/* origin is not a library */
		while(cp != cur_orgpath)
		{
			if(*cp == '/' || *cp == '\\') {
				*cp = '\0';
				break;
			}
			--cp;
		}
	}
	/* Check if the origin is in the system */
	orglen = mystrlen(cur_orgpath);
	if(cffind(origins, cur_orgpath, orglen, &orgsn) >= FOUND)
	{/* It is in the system, get the serial number */
		cur_org.sn = orgsn.a0;
	}
	else
	{/* Enter this origin into the system */
		orgsn.item = new_sn("SN1");
		orgsn.a2.type = STO_VALUE;
		cur_org.sn = orgsn.a0;

		/* Store the origin, indexed both ways */
		cfinsert(origins, cur_orgpath, orglen, &orgsn);
		cfput(qorigins, &orgsn, 4, cur_orgpath, orglen+1, NULL);
	}
	if(*cp == 0) *cp = '/';
}
static void
insert_file(char *path)
{
void *desc;

	/* Open the input file */
	if((desc = cfopen(path, F_RDONLY, NULL)) != NULL)
	{
	int numobjs;
	int i, j;
	char *objname;
	char *sympack;
	int packx;
	void *scratch;
	CFSTAT s;

		cfstat(desc, &s);
		setup_origin(s.st_mtime);

		/* Create a scratch object in memory */
		scratch = make_scratch();

		/* Take a look at the input file */
		numobjs = oxlink_scan_file(desc, scratch);		

		/* oxlink_scan_file returns results in 'scratch' 
			and on the 'scratch' stack */
		for(i = 0; i < numobjs; ++i, free(objname), free(sympack))
		{
		unsigned long numsyms;
		unsigned long symsize;
		unsigned long objsize;
		unsigned long objoffset;
		unsigned long objnum;
		unsigned long objnamlen;
		SymItem objval;
		int objexists = 0;
		int clash = 0;

			cfpop_value(scratch, &symsize);
			cfpop_value(scratch, &numsyms);
			cfpop_value(scratch, &objnum);
			cfpop_value(scratch, &objnamlen);
			if(objnamlen > 0) {
				objname = malloc(objnamlen);
				cfpop_data(scratch, objname, objnamlen);
			} else {
				objname = fileof(path);
				objnamlen = mystrlen(objname)+1;
			}
			cfpop_value(scratch, &objsize);
			cfpop_value(scratch, &objoffset);

			/* Get space for a chunk of symbols */
			sympack = malloc(symsize+1);
			packx = 0;

			if((objnamlen + curliblen) > 255) {
				cfprintf("%s -- name too long. CONTINUING\n", objname);
				continue;
			}
			 /* Check the object file for current existence */
			curobjlen = cfsprintf(curobj, "%s/%s", curlib, objname);
			if(cffind(ipaths, curobj, curobjlen, &objval) >= FOUND) {
				objexists = 1;
				if(!(act & (arREPLACE|arUPDATE))) {
					cfprintf("%s found in archive. CONTINUING\n", curobj);
					continue;
				}
			}
			/* Pass 1 over the symbols, check for clashes */ 
			for(j = 0; j < numsyms; ++j)
			{/* The global symbols for each .o file have been saved
					in 'sratch', keyed by (objnum<<18+symnum) */
			Item symptr;
			long key = (objnum<<18)+j;
			SymItem symval;
			int x;
				if(cffind(scratch, &key, 4, &symptr) == FOUND)
				{
				char *cp = cflocalize(scratch, &symptr);
					if(cp) {
						/* Add to the quick access pack of symbols */
						x = mystrcpy(&sympack[packx], cp)+1;
						if(!objexists)
						{/* Check for symbol clash with other .o files in lib */
						long dupnum = 0;
							for(;;)
							{/* Duplicate symbols are allowed, in separate libs */
							int	result = cffind_dupnum(msyms, cp, x-1, &symval, &dupnum);
								if(result >= FOUND)
								{
									if(		symval.libtype == libtype
										&&	symval.libnum == libnum)
									{
										clash = 1;
									}
									if(!clash && result > FOUND)
									{/* Duplicate symbols exist */
										++dupnum;
										continue;
									} else break;
								} else break;
							}
						}
						cfrelease(cp, R_CLEAN);
						if(clash) {
						char pathname[256];

							cfget(qipaths, &symval, 8, pathname, 256);
							cfprintf("cfar: symbol %s in %s clashes with %s. CONTINUING\n",
								&sympack[packx], objname, pathname);
							break;
						}
						packx += x;
					}
				}
			}/* END: symbol pass 1 */
			if(clash || !(act & (arADD|arREPLACE|arUPDATE)))
				continue;

			/* NOW add or replace the .o file */
			if(verbose || act == arUPDATE) {
				if(act == arUPDATE)
					 cfprintf("updating %s\n",objname);
				else cfprintf("%s\n", objname);
			}
			if(objexists) {
				delete_file(objname, 1);	
			} else {/* Get a new serial number for the .o file */
				objval.sn = new_sn("SN0");				
				objval.libnum = libnum;
				objval.libtype = libtype;
				objval.unused = 0;
				objval.tag = STO_VALUE;
			}

			/* Store the .o file */
			{
			Item space;
			char *loc;
				if(cfgetspace(lib, objsize, &space) != NULL)
				{
					if((loc = cflocalize(lib, &space)) != NULL) {
						cfseek(desc, objoffset, S_SET);
						cfread(desc, loc, objsize);
						cfrelease(loc, R_DIRTY);
						cfinsert(lib, objname, objnamlen-1, &space);
					} else {
						cfretspace(lib, &space);
						cfprintf("cfar: ERROR can't localize space for %s\n", objname);
					}
				} else cfprintf("cfar: ERROR can't allocate space for %s\n", objname);
			}

			/* Store the quick access package of symbols */
			objname[objnamlen-2] = 'x';
			sympack[symsize] = 0;
			cfput(lib, objname, objnamlen-1, sympack, symsize+1, NULL); 

			/* Store the backlink to the origin, time + origin serial number */
			objname[objnamlen-2] = 't';
			cfinsert(lib, objname, objnamlen-1, &cur_org);

			/* Store the path, indexed both ways */
			cfinsert(ipaths, curobj, curobjlen, &objval);
			cfput(qipaths, &objval, 8, curobj, curobjlen+1, NULL);

			/* Store the symbols, symname is the key, objval is the item */
			/* Pass 2 over the symbols */ 
			for(j = 0; j < numsyms; ++j)
			{/* The global symbols for each .o file have been saved
					in 'scratch', keyed by (objnum<<18+symnum) */
			Item symptr;
			long key = (objnum<<18)+j;
				if(cffind(scratch, &key, 4, &symptr) == FOUND)
				{
				char *cp;
					if((cp = cflocalize(scratch, &symptr)) != NULL) {
						cfinsert_dupnum(msyms, cp, mystrlen(cp), &objval, NULL);
						cfrelease(cp, R_CLEAN);
					} else {
						cfprintf("cfar: ERROR can't localize space for symbol %d\n", j);
					}
				}
			}
		}/* END: for(numobjs) */
		cfclose(scratch);
		cfclose(desc);
	}/* END: if(desc) */
	else cfprintf("cfar: can't open file %s, CONTINUING\n", path);
}
static void
proc_file(char *path)
{
	if(issymbol && (act == arCHECK)) {
		objof(path);
		return;
	}
	if(act & arEXTRACT)
		extract_file(path);
	if(act & arDELETE)
		delete_file(path, 0);
	if(act & (arADD|arREPLACE|arCHECK|arUPDATE))
		insert_file(path);
}
static void
print_map(char *name)
{
char path[256];
int i, j;

	cfprintf("MAP of %s\n", name);
	if(map_entries) {
		cfprintentries(name);
		cfsprintf(path, "%s/ipaths", name);
		cfprintentries(path);
		cfsprintf(path, "%s/qipaths", name);
		cfprintentries(path);
		cfsprintf(path, "%s/origins", name);
		cfprintentries(path);
		cfsprintf(path, "%s/qorigins", name);
		cfprintentries(path);
		cfsprintf(path, "%s/msyms", name);
		cfprintentries(path);
	}
	/* Print a list of origins */
	cfprintf("\n-------- OUTSIDE ORIGINS ----------\n\n");
	cfsprintf(path, "%s/origins", name);
	if((origins = cfopen(path, F_STAT, NULL)) != NULL)
	{
	Item orgnum;
		if(cfhead(origins, &orgnum) == OK)
		{
			do {
				mymemclr(path, 255);
				cfkey(origins, path, 255);
				cfprintf("id:%u\t%s\n", orgnum.a0, path);			
			} while(cfnext(origins, &orgnum) == OK);
		}	
		cfclose(origins);
	}
	/* Print the various libraries and entries */
	cfprintf("\n-------- LIBRARIES ----------\n\n");

	get_libtypes(name);
	for(i = 0; i < MAXLIBTYPE; ++i)
	{
		if(!ltypes[i])
			continue;
		cfsprintf(path, "%s/ltype%d", name, i);
		cfprintf("\nLibrary type %d\n", i);
		get_libnums(path);
		if(map_entries) {
			cfprintf("------------ RAW ENTRIES -----------\n");
			cfprintentries(path);
		}
		for(j = 0; j < 4096; ++j)
		{
		void *clib;
			if(!lnums[j])
				continue;
			cfsprintf(path, "%s/ltype%d/lib%d", name, i, j);
			if((clib = cfopen(path, F_STAT, NULL)) != NULL) {
				if(verbose)
					cfprintf("------- lib%d  %d bytes -------\n",
						j, cfbytesused(clib));
				else
					cfprintf("------- lib%d --------\n", j);
				cfhead(clib, NULL);
				do {
				char key[256];
				int keylen;
					mymemclr(key,256);
					cfkey(clib, key, 255);
					keylen = mystrlen(key);
					if(key[keylen-1] == 'o') {
						if(verbose) {
						int dlen;
						OrgItem oi;
							cfdatalen(clib, &dlen);
							key[keylen-1] = 't';
							cffind(clib, key, keylen, &oi);
							key[keylen-1] = 'o';
							cfprintf("    %s  bytes:%u orgid:(%u) date:%s", 
								key, dlen, oi.sn, ctime(&oi.filetime));
						}
						else
							cfprintf("     %s\n", key);
						if(verbose) {
						Item pakval;
						char *cp, *sp;
							key[keylen-1] = 'x';
							cfmark(clib);
							cffind(clib, key, keylen, &pakval);
							cp = cflocalize(clib, &pakval);
							for(sp=cp; *sp; sp += mystrlen(sp)+1)
							{
								cfprintf("        %s\n", sp);
							}
							cfrelease(cp, R_CLEAN);
							cffind_mark(clib, NULL);
						}
					}
				} while (cfnext(clib, NULL) == OK);
				if(map_entries) {
					cfprintf("------------ RAW ENTRIES -----------\n");
					cfprintentries(clib);
				}
				cfclose(clib);
			}
		}
	}
	if(map_bitmaps) {
		cfprintf("\n------------ BITMAPS ---------------\n");
		cfprintbitmaps(name);
	}
}
static void
usage()
{
cfprintf("\
Usage: cfar [-acdflmrstvxMS] archive [files ...]\n\
    Switch ------ Meaning\n\
    a             Add file , do not overwrite\n\
    r             Replace/Add file\n\
    d             Delete file\n\
    x             Extract file\n\
    f             Freshen the archive (replace outdated files)\n\
    c             Check input files against library for clashes\n\
    t#            Set libtype # 0=module, 1=class, 2=subsystem, max:15\n\
    l#            Place file in libnum #, max:4095\n\
    s             'file' is a symbol name not a file name (for extract/delete)\n\
    m             Print archive map\n\
    M             Print a makefile which would recreate the archive\n\
    v             Verbose\n\
    S             Shrink archive\n\
");
}
static void
shrink_archive(char *name)
{
void *x;
void *y;

	cfprintf("cfar: shrink_archive\n");
	if(verbose)
		cfprintf("cfar: copy in\n");
	x = cfcopy("MEMORY/shrink", name);
	if(verbose)
		cfprintf("cfar: copy out\n");
	y = cfcopy(name, x);
	if(verbose)
		cfprintf("cfar: clean up\n");
	cfunlink(x, NULL);
	cfclose(y);
}
static __inline__ void
stat_file(OrgItem *op, char *name, int namlen, long typ, long num, 
		char *arch, void *scratch)
{
Item orgtag;
char orgpath[256];
long orglen;
long sn = op->sn;

	cfget(qorigins, &sn, 4, &orgpath, 256);
	orglen = mystrlen(orgpath);
	if(orgpath[orglen-1] == 'a' && orgpath[orglen-2] == '.')
	{/* This origin is a library */
		/* Mark this origin or return if it has been marked */
		orgtag.a2.size = op->sn;
		orgtag.a0 = typ<<4 + num;
		orgtag.a2.type = STO_VALUE;
		if(cfinsert(scratch, &orgtag, 8, &orgtag) != OK)
			return;
	}
	else
	{/* This origin is a directory, with an embedded file */
		orgpath[orglen] = '/';
		++orglen;
		orglen += mystrcpy(&orgpath[orglen], name);
	}
	/* The origin has not been examined, do it */
	if(freshen) {
	CFSTAT s;
		if(cfstat(orgpath, &s) == OK)
		{
			if(s.st_mtime > op->filetime)
			{ /* REFRESH THIS ITEM */
				++orglen;
				cfpush_data(scratch, orgpath, orglen);
				cfpush_value(scratch, &orglen);
				cfpush_value(scratch, &typ);
				cfpush_value(scratch, &num);
			}
		}
		else
			cfprintf("cannot stat %s, CONTINUING\n", orgpath);
	}
	if(makefile)
		cfprintf("cfar -rt%dl%d %s %s\n", typ, num, arch, orgpath);
}
static void
scan_archive(char *name, void *scratch)
{
int i;

	get_libtypes(name);
	for(i = 0; i < MAXLIBTYPE; ++i)
	{
	char path[256];
	int j;
		if(!ltypes[i])
			continue;
		cfsprintf(path, "%s/ltype%d", name, i);
		get_libnums(path);
		for(j = 0; j < 4096; ++j)
		{
		void *clib;
			if(!lnums[j])
				continue;
			cfsprintf(path, "%s/ltype%d/lib%d", name, i, j);
			if((clib = cfopen(path, F_STAT, NULL)) != NULL) {
			OrgItem oi;
				cfhead(clib, &oi);
				do {
				char key[256];
				int keylen;
					mymemclr(key,256);
					cfkey(clib, key, 255);
					keylen = mystrlen(key);
					if(key[keylen-1] == 't') {
						key[keylen-1] = 'o';
						stat_file(&oi, key, keylen, i, j, name, scratch);
					}
				} while (cfnext(clib, &oi) == OK);
				cfclose(clib);
			}
		}
	}
}
static void
setup_version(int flag, char *path)
{
KeyItem snval = 0;
	cfinsert(archive, "ArVer", 5, &prgversion);
	cfinsert(archive, "SN0", 3, &snval);
	cfinsert(archive, "SN1", 3, &snval);
	cfinsert(archive, "SN2", 3, &snval);
	if(flag)
		 cfprintf("cfar: add archive property to %s\n", path);
	else cfprintf("cfar: create archive %s\n", path);
}
#ifdef SKELETON
int
cfar(int argc, char **argv)
#else
void
main(int argc, char **argv)
#endif
{
int err = 0;
int i,j;
char buf[200];

#ifndef SKELETON
	cfinit("cfar", 400, NULL);
	if((err = oxlink_init(cf_find_file(argv[0], buf))) == 0)
#endif
	{
		/* Get the switches */
		for(i = 1; i < argc; ++i)
		{
			if(argv[i][0] == '-')
			{
				for(j=1; argv[i][j]; ++j)
				{
					switch(argv[i][j])
					{
						case 'f':
							freshen = 1;
							break;
						case 'r':
							act = arREPLACE;
							break;
						case 'a':
							act = arADD;
							break;
						case 'x':
							act |= arEXTRACT;
							break;
						case 'd':
							act |= arDELETE;
							break;
						case 'v':
							verbose = 1;
							break;
						case 'c':
							act = arCHECK;
							break;
						case 'S':
							shrink = 1;
							break;
						case 'l':
							myatol(&argv[i][j+1], &libnum);
							if(libnum < 0 || libnum > 4095)
								err = 1;
							break;
						case 't':
							myatol(&argv[i][j+1], &libtype);
							if(libtype < 0 || libtype >= MAXLIBTYPE)
								err = 1;
							break;
						case 'm':
							map = 1;
							break;
						case 's':
							issymbol = 1;
							break;
						case 'B':
							map_bitmaps = 1;
							break;
						case 'E':
							map_entries = 1;
							break;
						case 'M':
							makefile = 1;
							break;
						default:
							break;
					}
				}/* END: for(j) */
				/* Trim switch */
				for(j = i; j < argc-1; ++j)
					argv[j] = argv[j+1];
				--argc; 			
				--i;
			}/* END: if('-') */
		}/* END: for(argc) (get switches) */
		if(verbose) {
			cfprintf("cff archiver Version:%u Copyright 1993 Norman D. Culver\n", prgversion.a0);
		}
		if(err) {
			usage();
		}
		/* Get the archive */
		if(!err && ((map|shrink|freshen|makefile) || (act && argc > 2))) {
		int mode = F_RDWR;
		int new = 0;
		  if(act & (arADD|arREPLACE))
			mode |= F_CREAT;
#ifdef SKELETON
		  oxlink_nouse_library(argv[1]);
#endif
		  if((archive = cfopen(argv[1], mode, NULL)) != NULL) {
			if(!(cfobtype(archive) & OB_XFILE)) {
			  if(cfisnew(archive))
			  {/* new file, set up version and serial numbers */
				setup_version(0, argv[1]);
				new = 1;
			  } else {
				if(cffind(archive, "ArVer", 5, &arversion) < FOUND) 
				{/* old .cff file without archive property, enable it */
					setup_version(1, argv[1]);
					new = 1;
				}
			  }
			  if(cffind(archive, "ArVer", 5, &arversion) >= FOUND) {
			    if(arversion.a0 <= prgversion.a0) {
				  ipaths = cfsubopen(archive, "ipaths", F_RDWR|F_CREAT|F_SORTED, NULL);
				  qipaths = cfsubopen(archive, "qipaths", F_RDWR|F_CREAT, NULL);
				  origins = cfsubopen(archive, "origins", F_RDWR|F_CREAT|F_SORTED|F_BIGDIR, NULL);
				  qorigins = cfsubopen(archive, "qorigins", F_RDWR|F_CREAT, NULL);
				  msyms = cfsubopen(archive, "msyms", F_RDWR|F_CREAT|F_SORTED|F_HUGEDIR, NULL);

				  if(!ipaths) {
					cfprintf("cfar: can't open subdir 'ipaths'\n");
					goto xt;
				  }
				  if(!qipaths) {
					cfprintf("cfar: can't open subdir 'ipaths'\n");
					goto xt;
				  }
				  if(!origins) {
					cfprintf("cfar: can't open subdir 'origins'\n");
					goto xt;
				  }
				  if(!qorigins) {
					cfprintf("cfar: can't open subdir 'qorigins'\n");
					goto xt;
				  }
				  if(!msyms) {
					cfprintf("cfar: can't open subdir 'msyms'\n");
					goto xt;
				  }
				  cfsetlazy(msyms);
				  cfsetlazy(ipaths);
				  cfsetlazy(qipaths);
				  cfsetlazy(origins);
				  cfsetlazy(qorigins);

				if(argc < 3 && (freshen|makefile) && !new) {
				void *scratch = make_scratch();
				  scan_archive(argv[1], scratch);			
				  act = arUPDATE;
				  while(cfstackdepth(scratch) > 0)
				  {
				  long filelen;
					cfpop_value(scratch, &libnum);
					cfpop_value(scratch, &libtype);
					cfpop_value(scratch, &filelen);
					cur_orgpath = malloc(filelen);
					cfpop_data(scratch, cur_orgpath, filelen);
					if(open_curlib())
					{/* Freshen a file */
						proc_file(cur_orgpath);
					}
					free(cur_orgpath);
				  }
				  cfclose(scratch);
				} else {
				  if(open_curlib())
				  {/* Process the input files */
					  for(i = 2; i < argc; ++i)
					  {
						if(!issymbol)
							mystrlower(argv[i]);
						cur_orgtype = cfpathtrn(argv[i], &cur_orgpath);
						proc_file(argv[i]);
						free(cur_orgpath);
					  }		
					}
				}
				cfclrlazy(lib);
				cfclrlazy(msyms);
				cfclrlazy(ipaths);
				cfclrlazy(qipaths);
				cfclrlazy(origins);
				cfclrlazy(qorigins);

				cfclose(lib);
				cfclose(plib);
				cfclose(msyms);
				cfclose(ipaths);
				cfclose(qipaths);
				cfclose(origins);
				cfclose(qorigins);
				cfclose(archive);

				if(shrink) shrink_archive(argv[1]);
				if(map) print_map(argv[1]);

			  } else cfprintf("cfar: a version %u archive cannot be processed by a version %u program\n", arversion.a0, prgversion.a0);
			} else cfprintf("cfar: %s is not an archive\n", argv[1]);
		  } else cfprintf("cfar: %s is not a cff database\n", argv[1]);
		} else cfprintf("cfar: can't open archive %s\n", argv[1]);
	  } else {cfprintf("cfar: insufficient args\n"); usage();}
	}
#ifndef SKELETON
	else cfprintf("cfar: cannot find executable %s\n", argv[0]);
#endif
xt:
#ifndef SKELETON
	cfexit();
	exit(0);
#else
	return 0;
#endif
}
