/* ==== fc_init.c ============================================================
 * Copyright (c) 1995 by Chris Provenzano, proven@mit.edu
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *  This product includes software developed by Chris Provenzano.
 * 4. The name of Chris Provenzano may not be used to endorse or promote 
 *	  products derived from this software without specific prior written
 *	  permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL CHRIS PROVENZANO BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 *
 * Description : The fc initializer functions.
 *
 *  1.00 95/06/28  proven
 *      -Started coding this file.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include "util.h"
#include "fc.h"

#define FD_CREATE_FLAGS		(O_RDWR | O_CREAT | O_EXCL)

/* ==========================================================================
 * fc_init_keys()
 */
static int fc_init_keys(struct fc * fc)
{
	struct stat statbuf;
	int key_fd;

	if (stat(FC_KEY_FILE, &statbuf) < OK) { 
		fc->keycount = 0;
		fc->keydata = NULL;
		fc->keys = NULL;
	} else {
		if ((key_fd = open(FC_KEY_FILE, O_RDWR)) < OK) {
			return(NOTOK);
		}
		if ((fc->keydata = (char *)malloc(statbuf.st_size)) == NULL) {
			close(key_fd);
			return(NOTOK);
		}
		if (read(key_fd, fc->keydata, statbuf.st_size) != statbuf.st_size) {
			free(fc->keydata);
			close(key_fd);
			return(NOTOK);
		}
		close(key_fd);

		if (fc->keycount = atoi(fc->keydata)) {
			struct fc_keys * fc_key;
			int fc_key_count, i = 0;

			if ((fc->keys = fc_key_alloc(fc->keycount)) == NULL) {
				free(fc->keydata);
				return(NOTOK);
			}
			while (++i < statbuf.st_size) {
				if (isspace(fc->keydata[i]))
					break;
			}
			while (++i < statbuf.st_size) {
				if (!isspace(fc->keydata[i]))
					break;
			}
			/* 
			 * We come to the first key 
			 * We really need a better parsing algorithm
			 */
			fc_key_count = 0;
			fc_key = fc->keys;
			while ((fc_key_count < fc->keycount) && (i < statbuf.st_size)) {
				fc_key->alloctype = BLOCK_ALLOCED;
				fc_key->key_name = fc->keydata + i;
				fc_key->key_num	= ++fc_key_count;
				fc_key->filekey_first = NULL;
				fc_key->file_first = NULL;
				fc_key->file_count = 0;

				while (++i < statbuf.st_size) {
					if (isspace(fc->keydata[i])) {
						fc->keydata[i] = '\0';
						break;
					}
				}
				while (++i < statbuf.st_size) {
					if (!isspace(fc->keydata[i]))
						break;
				}
				fc_key = fc_key->key_next;
			}
			if (fc_key_count != fc->keycount) {
				free(fc->keydata);
				return(NOTOK);
			}
		} else {
			fc->keydata = NULL;
			fc->keys = NULL;
		}
		if (rename(FC_KEY_FILE, FC_KEY_BKUP) < OK) {
			printf("Couldn't make backup of %s\n", FC_KEY_FILE);
			return(NOTOK);
		}
	}
	if ((key_fd = open(FC_KEY_FILE, FD_CREATE_FLAGS, 0644)) < OK) {
		return(NOTOK);
	}
	write(key_fd, "0\n", 2);
	close(key_fd);
	return(OK);
}

/* ==========================================================================
 * fc_init_filekeys()
 */
static inline int fc_init_filekeys(struct fc * fc, struct fc_files * fc_file,
  int * offset, int filedata_size)
{
	struct fc_filekeys * fc_filekey;
	struct fc_keys * fc_key;
	int fc_filekey_count;
	char * key_name;

	if (fc_file->filekey_count = atoi(fc->filedata + *offset)) {
		/* Clear past the keycount */
		while ((++(*offset)) < filedata_size) {
			if (fc->filedata[*offset] == ':') {
				fc->filedata[*offset] = '\0';
				break;
			}
		}

		/* Allocate space for the array of struct fc_keys */
		if ((fc_file->filekey_first = (struct fc_filekeys *)malloc(
		  fc_file->filekey_count * sizeof(struct fc_filekeys))) == NULL) {
			return(NOTOK);
		}

		fc_filekey_count = 0;
		fc_filekey = fc_file->filekey_first;
		while ((fc_filekey_count < fc_file->filekey_count) && 
		  ((++(*offset)) < filedata_size)) {
			key_name = fc->filedata + *offset;
			while ((++(*offset)) < filedata_size) {
				if (fc->filedata[*offset] == ':') {
					fc->filedata[*offset] = '\0';
					break;
				}
			}
		
			if ((*offset) < filedata_size) {
				/* Add file to linked list for the given key */
				for (fc_key = fc->keys; fc_key; fc_key = fc_key->key_next) {
					if (!strcmp(fc_key->key_name, key_name)) {
						fc_filekey->filekey_next = fc_key->filekey_first;
						fc_filekey->file_next = fc_key->file_first;
						fc_key->filekey_first = fc_filekey;
						fc_key->file_first = fc_file;
						fc_filekey->key = fc_key;
						fc_key->file_count++;
						break;
					}
				}
				fc_filekey_count++;
				fc_filekey++;
			}
		}
		if ((fc_filekey_count != fc_file->filekey_count) ||
		  ((++(*offset)) == filedata_size)) {
			return(NOTOK);
		}
	} else {
		abort();
	}
	return(OK);
}

/* ==========================================================================
 * fc_init_files()
 */
static int fc_init_files(struct fc * fc)
{
	struct stat statbuf;
	int file_fd;

	if (stat(FC_FILE_FILE, &statbuf) < 0) { 
		fc->filecount = 0;
		fc->filedata = NULL;
		fc->files = NULL;
	} else {
		if ((file_fd = open(FC_FILE_FILE, O_RDWR)) < OK) {
			return(NOTOK);
		}
		if ((fc->filedata = (char *)malloc(statbuf.st_size)) == NULL) {
			close(file_fd);
			return(NOTOK);
		}
		if (read(file_fd, fc->filedata, statbuf.st_size) != statbuf.st_size){
			free(fc->filedata);
			close(file_fd);
			return(NOTOK);
		}
		close(file_fd);
		if (fc->filecount = atoi(fc->filedata)) {
			struct fc_files * fc_file;
			int fc_file_count, i = 0;

			if ((fc->files = fc_file_alloc(fc->filecount)) == NULL) {
				free(fc->filedata);
				return(NOTOK);
			}
			while (++i < statbuf.st_size) {
				if (isspace(fc->filedata[i]))
					break;
			}
			while (++i < statbuf.st_size) {
				if (!isspace(fc->filedata[i]))
					break;
			}
			/* 
			 * We come to the first file 
			 * We really need a better parsing algorithm
			 * Files are stored as ...
			 * keycount:key:key:...:key:filename
			 */
			fc_file_count = 0;
			fc_file = fc->files;
			while ((fc_file_count < fc->filecount) && (i < statbuf.st_size)) {
			  	if (fc_init_filekeys(fc, fc_file, &i, statbuf.st_size)) 
					break;
				fc_file->alloctype = BLOCK_ALLOCED;
				fc_file->file_name = fc->filedata + i;
				fc_file = fc_file->file_next;
				fc_file_count++;

				while (++i < statbuf.st_size) {
					if (isspace(fc->filedata[i])) {
						fc->filedata[i] = '\0';
						break;
					}
				}
				while (++i < statbuf.st_size) {
					if (!isspace(fc->filedata[i]))
						break;
				}
			}
			if (fc_file_count != fc->filecount) {
				free(fc->filedata);
				return(NOTOK);
			}
		} else {
			fc->filedata = NULL;
			fc->files = NULL;
		}

		/* Done parsing do backup */
		if (rename(FC_FILE_FILE, FC_FILE_BKUP) < OK) {
			printf("Couldn't make backup of %s\n", FC_FILE_FILE);
			return(NOTOK);
		}
	}
	if ((file_fd = open(FC_FILE_FILE, FD_CREATE_FLAGS, 0644)) < OK) {
		return(NOTOK);
	}
	write(file_fd, "0\n", 2);
	close(file_fd);
	return(OK);
}
  
/* ==========================================================================
 * fc_init_groupkeys()
 */
int fc_init_groupkeys(struct fc * fc, struct fc_groups * fc_group,
  int * offset, int groupdata_size)
{
	struct fc_keys * fc_key;
	int fc_groupkey_count;
	char * key_name;

	fc_group->groupkey_count = atoi(fc->groupdata + *offset);
	while ((++(*offset)) < groupdata_size) {
		if (fc->groupdata[*offset] == ':') {
			fc->groupdata[*offset] = '\0';
			break;
		}
	}

	fc_groupkey_count = 0;
    if (fc_group->groupkey_count) {
		/* Allocate space for the array of struct fc_keys */
		if ((fc_group->groupkey_first = (struct fc_keys **)malloc(
		  fc_group->groupkey_count * sizeof(struct fc_keys *))) == NULL) {
			return(NOTOK);
		}

		while ((fc_groupkey_count < fc_group->groupkey_count) && 
		  ((++(*offset)) < groupdata_size)) {
			key_name = fc->groupdata + *offset;
			while ((++(*offset)) < groupdata_size) {
				if (fc->groupdata[*offset] == ':') {
					fc->groupdata[*offset] = '\0';
					break;
				}
			}
		
			if ((*offset) < groupdata_size) {
				/* Add file to linked list for the given key */
				for (fc_key = fc->keys; fc_key; fc_key = fc_key->key_next) {
					if (!strcmp(fc_key->key_name, key_name)) {
						fc_group->groupkey_first[fc_groupkey_count] = fc_key;
						break;
					}
				}
				fc_groupkey_count++;
			}
		}
	}
	if ((fc_groupkey_count != fc_group->groupkey_count) ||
	  ((++(*offset)) == groupdata_size)) {
		return(NOTOK);
	}
	return(OK);
}

/* ==========================================================================
 * fc_init_groups()
 */
static int fc_init_groups(struct fc * fc)
{
	struct stat statbuf;
	int group_fd;

	if (stat(FC_GROUP_FILE, &statbuf) < 0) { 
		fc->groupcount = 0;
		fc->groupdata = NULL;
		fc->groups = NULL;
	} else {
		if ((group_fd = open(FC_GROUP_FILE, O_RDWR)) < OK) {
			return(NOTOK);
		}
		if ((fc->groupdata = (char *)malloc(statbuf.st_size)) == NULL) {
			close(group_fd);
			return(NOTOK);
		}
		if (read(group_fd, fc->groupdata, statbuf.st_size) != statbuf.st_size){
			free(fc->groupdata);
			close(group_fd);
			return(NOTOK);
		}
		close(group_fd);
		if (fc->groupcount = atoi(fc->groupdata)) {
			struct fc_groups * fc_group;
			int fc_group_count, i = 0;

			if ((fc->groups = fc_group_alloc(fc->groupcount)) == NULL) {
				free(fc->groupdata);
				return(NOTOK);
			}
			while (++i < statbuf.st_size) {
				if (isspace(fc->groupdata[i]))
					break;
			}
			while (++i < statbuf.st_size) {
				if (!isspace(fc->groupdata[i]))
					break;
			}
			/* 
			 * We come to the first group 
			 * We really need a better parsing algorithm
			 * Groups are stored as ...
			 * keycount:key:key:...:key:group
			 */
			fc_group_count = 0;
			fc_group = fc->groups;
			while ((fc_group_count < fc->groupcount) && (i < statbuf.st_size)) {
			  	if (fc_init_groupkeys(fc, fc_group, &i, statbuf.st_size)) 
					break;
				fc_group->alloctype = BLOCK_ALLOCED;
				fc_group->group_name = fc->groupdata + i;
				fc_group = fc_group->group_next;
				fc_group_count++;

				while (++i < statbuf.st_size) {
					if (isspace(fc->groupdata[i])) {
						fc->groupdata[i] = '\0';
						break;
					}
				}
				while (++i < statbuf.st_size) {
					if (!isspace(fc->groupdata[i]))
						break;
				}
			}
			if (fc_group_count != fc->groupcount) {
				free(fc->groupdata);
				return(NOTOK);
			}
		} else {
			fc->groupdata = NULL;
			fc->groups = NULL;
		}

		/* Done parsing do backup */
		if (rename(FC_GROUP_FILE, FC_GROUP_BKUP) < OK) {
			printf("Couldn't make backup of %s\n", FC_GROUP_FILE);
			return(NOTOK);
		}
	}
	if ((group_fd = open(FC_GROUP_FILE, FD_CREATE_FLAGS, 0644)) < OK) {
		return(NOTOK);
	}
	write(group_fd, "0\n", 2);
	close(group_fd);
	return(OK);
}
  
/* ==========================================================================
 * fc_init()
 */
fc_init(struct fc * fc)
{
	if ((fc_key_init() == OK)&&(fc_file_init() == OK)&&(fc_group_init() == OK)&&
	  (fc_init_keys(fc) == OK)&&(fc_init_files(fc) == OK)&&
	  (fc_init_groups(fc) == OK)) {
		return(OK);
	}
	return(NOTOK);
}

