/* cfin.c -- load files into a cff filesystem */

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include "cff.h"

static int chunk;
static int newname;
static int replace;
static void *it;
static void *subit;
static char basefile[200];
static char midfile[200];
static char *midpart;
static char *lastpart;

static void
Usage()
{
cfputs(
"Usage: cfin -cnr cfpath files ...\n"
"   Option     Meaning\n"
"   -c         create a chunk\n"
"   -n         give the input file a new name (last part of cfpath)\n"
"   -r         replace old data\n"
"   -R         unconditionally replace old data\n"
);
}
static void
lose_backslashes(char *cp)
{
	while(*cp) {
		if(*cp == '\\')
			*cp = '/';
		++cp;
	}
}
static void
lower_string(char *cp)
{
	while(*cp) {
		tolower(*cp);
		++cp;
	}
}
static void
proc_file(char *filepath)
{
void *fit;
void *h = (subit) ? subit : it;
void *fd;
char *filename;
char *cfname;
Item item;
CFSTAT sbuf;
int exists = 0;
char buf[4096];

	lose_backslashes(filepath);
	lower_string(filepath);
	filename = strrchr(filepath, '/');
	if(filename)
		++filename;
	else filename = filepath;
	if(newname)
		cfname = lastpart;
	else
		cfname = filename;

	/* check for existance of the item */
	if(newname && !chunk) {
		fd = h;
	}
	else if(cffind(h, cfname, strlen(cfname), &item) >= FOUND) {
		exists = 1;
		if(!replace) {
			cfprintf("cfin: %s exists and replace not enabled\n", cfname);
			return;
		}
		if(cfsubstat(h, cfname, &sbuf) == OK)
		{
			if(		((sbuf.st_mode & M_CHUNK) && !chunk)
				||	(!(sbuf.st_mode & M_CHUNK) && chunk))
			{
				if(replace == 2)
				{/* unconditionally replace */
					if(sbuf.st_mode & M_CHUNK)
						cfdelete(h, cfname, strlen(cfname));
					else {
					void *df = cfsubopen(h, cfname, F_RDWR, NULL);
						cfunlink(df, NULL);
					}
					exists = 0;
				}
				cfprintf("cfin: %s exists with incommensurate data type\n", cfname);
				return;
			}
		} else {
			cfprintf("cfin: can't stat %s\n", cfname);
			return;
		}
	}
	if((fit = cfopen(filepath, F_RDONLY, NULL)) != NULL)
	{
	int amount;
		if(chunk)
		{
		int filesize = cfseek(fit, 0, S_END);
		Item citem;
			cfseek(fit, 0, S_SET);
			if(exists) {
				cfdelete(h, cfname, strlen(cfname));
			}
			if(cfgetspace(h, filesize, &citem) == NULL)
			{
				cfprintf("cfin: cannot allocate %d bytes for %s\n", filesize, cfname);
				cfclose(fit);
				return;
			}
			cfinsert(h, cfname, strlen(cfname), &citem);
			fd = cfopen_chunk(h, &citem);			
		}
		else if(!newname)
		{
			fd = cfsubopen(h, cfname, F_RDWR|F_CREAT|F_TRUNC|F_FILEONLY, NULL);
		}
		if(fd == NULL) {
			cfprintf("cfin: could not create %s\n", cfname);
			cfclose(fit);
			return;
		}
		mymemclr(buf, sizeof(buf));
		while((amount = cfread(fit, buf, sizeof(buf))) > 0) {
			if(chunk)
				amount = round_up(amount, 32);
			cfwrite(fd, buf, amount);
			mymemclr(buf, sizeof(buf));
		}
		if(fd != h)
			cfclose(fd);
		cfclose(fit);
	} else {
		cfprintf("cfin: input file %s not found\n", filepath);
	}
}
static void *
create_middle(char *path, void *itt)
{
char midpath[200];
char *cp;

	strcpy(midpath, path);
	cp = midpath;
	while((cp = (char*)strchr(cp, '/')) != NULL)
	{
	void *subitt;
		*cp = '\0';		
		subitt = cfsubopen(itt, midpath, F_RDWR|F_CREAT, NULL);
		cfclose(subitt);
		*cp = '/';
		++cp;
	} 
	return cfsubopen(itt, midpath, F_RDWR|F_CREAT, NULL);
}
static void *
open_middle(void *itt)
{
void *subitt = NULL;

	if(!midpart)
	{
		if(lastpart)
		{
			if(!chunk)
			{
			int trunc = (newname) ? F_TRUNC : 0;
			  if(newname && !replace) {
				if(cffind(itt, lastpart, strlen(lastpart), NULL) >= FOUND) {
				  cfprintf("Cfin: %s exists and replace not enabled\n", lastpart);
					return NULL;
				}
			  }
			  subitt = cfsubopen(itt, lastpart, F_RDWR|F_CREAT|trunc, NULL);
			} else {
				subitt = cfsubopen(itt, lastpart, F_RDWR|F_CREAT, NULL);
			}
		}
	}
	else if(newname && chunk)
	{
		subitt = cfsubopen(itt, midfile, F_RDWR|F_CREAT, NULL);
		if(subitt == NULL)
		{/* create the path in pieces */
			subitt = create_middle(midfile, itt);
		}
	}
	else if(newname)
	{
		subitt = cfsubopen(itt, midpart, F_RDWR|F_CREAT|F_TRUNC, NULL);
		if(subitt == NULL)
		{/* create the path in pieces */
			subitt = create_middle(midpart, itt);
		}
	}
	else
	{
		subitt = cfsubopen(itt, midpart, F_RDWR|F_CREAT, NULL);
		if(subitt == NULL)
		{/* create the path in pieces */
			subitt = create_middle(midpart, itt);
		}
	}
	return subitt;
}
static void *
open_cffile(char *name)
{
int namlen = strlen(name);
int i;
void *itt = NULL;
long obbits = OB_XFILE;
	
	if((lastpart = strrchr(name, '/')) != NULL)
		++lastpart;
	if(newname && !lastpart) {
		cfprintf("cfin: new filename enabled but not supplied\n");
		Usage();
		return NULL;
	}
	for(i = namlen-1; i >= 0; --i)
	{
		if(		name[i] == 'f'
			&&	name[i-1] == 'f'
			&&	name[i-2] == 'c'
			&&	name[i-3] == '.')
		{
#ifdef SKELETON
			oxlink_nouse_library(name);
#endif
			strncpy(basefile, name, i+1);
			if(&name[i] > lastpart)
				lastpart = NULL;
			else if(&name[i] != lastpart -2)
			{
				midpart = &name[i]+2;
				strncpy(midfile, midpart, (int)(lastpart-midpart-1));
			}

			obbits = cfobtype(basefile);
			if(obbits == 0) {
				itt = cfopen(basefile, F_RDWR|F_CREAT, NULL);
				if(itt == NULL)
					cfprintf("cfin: cannot create %s\n", basefile);
			} else if(obbits & OB_ROOTDIR) {
				itt = cfopen(basefile, F_RDWR, NULL);
			} else {
				cfprintf("cfin: %s is not a cff filesystem\n", basefile);
				return NULL;
			}
			break;
		}
	}
	if(obbits & OB_XFILE) {
		cfprintf("cfin: %s is not a cff filesystem\n", name);
		return NULL;
	}
	if(itt) {
		subit = open_middle(itt);
 	}
	return itt;
}
#ifdef SKELETON
int
cfin(int argc, char **argv)
#else
void
main(int argc, char **argv)
#endif
{
int i,j;

#ifndef SKELETON
	cfinit("cfin", 400, NULL);
#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 'c':
						chunk = 1;
						break;
					case 'n':
						newname = 1;
						break;
					case 'r':
						replace = 1;
						break;
					case 'R':
						replace = 2;
						break;
					case '?':
						Usage();
						exit(0);
					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(argc >= 3) {
		lose_backslashes(argv[1]);
		if((it = open_cffile(argv[1])) != (void*)0)
		{
			for(i = 2; i < argc; ++i)
				proc_file(argv[i]);
			if(subit)
				cfclose(subit);
			cfclose(it);
		}
	}
	else Usage();
#ifndef SKELETON
	cfexit();
	exit(0);
#else
	return 0;
#endif
}

