/* ==== fc_do_key.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 : Main fc functions.
 *
 *  1.00 95/06/28  proven
 *      -Started coding this file.
 */

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

/* TTT Remove printfs and use a generic error system. */

/* ==========================================================================
 * fc_do_ak()
 *
 * For now, only allow adding one key at a time.
 */
int fc_do_ak(struct fc * fc, char * data)
{
	struct fc_keys ** fc_key_p = &(fc->keys);
	struct fc_keys * fc_key;
	char * key_name;
	char * key;
	int i;

	if (key = parse_get_key(data, NULL)) {
		if (!parse_key_is_special(key)) {
			if (key_name = (char *)malloc(strlen(key) + 1)) {
				if (fc_key_insert(key, &(fc->keys), &fc_key) == OK) {
					fc_key->key_name = strcpy(key_name, key);
					fc_key->alloctype = IND_ALLOCED;
					fc_key->file_first = NULL;
					fc_key->file_count = 0;
					while (fc_key->key_next) {
						fc_key->key_num = fc_key->key_next->key_num;
						fc_key = fc_key->key_next;
					}
					fc_key->key_num = ++fc->keycount;
					return (OK);
				}
				printf("Error: adding key %s to key list\n", key);
				free(key_name);
			} else {
				printf("Can't alloc space for command %s\n", "ak");
			}
		} else {
			printf("Can't add reserved key %s for command %s\n", key, "ak");
		}
	} else {
		printf("Not enough args for command %s\n", "ak");
	}
	return(NOTOK);
}

/* ==========================================================================
 * fc_do_lk()
 *
 * If key == "." list the current keys which will still result in files
 * If key == NULL list all the keys
 */
int fc_do_lk(struct fc * fc, char * data)
{
	struct fc_listkeys * listkeys_current = NULL;
	struct fc_files * fc_file, * fc_file_next;
	struct fc_keys *fc_key, * keys = NULL;
    char * key;
	int i, size, count_current;

	if (key = parse_get_key(data, &data)) {
		if (count_current = fc->count_current) {
			struct fc_files * fc_files;

			size = count_current * sizeof(struct fc_listkeys);
			if ((listkeys_current = (struct fc_listkeys *)malloc(size))==NULL) {
				printf("Can't alloc space for command %s\n", "lk");
				return(NOTOK);
			}
			if (fc_files =fc_file_alloc(fc->listkeys_current->key->file_count)){
				for (i = 0; i < count_current; i++) {
					struct fc_files * listfile;

					/* XXX This is ugly and needs cleaning up --proven */
					listkeys_current[i].key = fc->listkeys_current[i].key;
					if (listfile = fc->listkeys_current[i].file_first) {
						fc_file = fc_files;
						while (listfile) {
							fc_file->alloctype = OTHER_ALLOCED;
							fc_file->file_name = listfile->file_name;
							fc_file->filekey_first = listfile->filekey_first;
							fc_file->filekey_count = listfile->filekey_count;
							if (listfile = listfile->file_next) {
								fc_file = fc_file->file_next;
							} else {
								fc_files = fc_file->file_next;
								fc_file->file_next = NULL;
								break;
							}
						}
					} else {
						listkeys_current[i].file_first = NULL;
						listkeys_current[i].file_last = NULL;
					}
					listkeys_current[i].file_count = 
					  fc->listkeys_current[i].file_count;
				}
			} else {
				printf("Can't alloc space for command %s\n", "lk");
				free (listkeys_current);
				return(NOTOK);
			}
		}
		do {
			switch (parse_key_is_special(key)) {
			case 3: /* List all the keys that result in files */
				if (count_current) {
					for (i = 0; i < count_current; i++) {
						for (fc_file = listkeys_current[i].file_first; 
						  fc_file; fc_file = fc_file_next) {
							fc_file_next = fc_file->file_next;
							fc_file_free(fc_file);
						}
					}
					count_current = 0;
					free(listkeys_current);
					listkeys_current = NULL;
				}
			case 1: /* List all the current keys that result in files */
				break;
			case 2: /* Remove the top current key */
				switch (count_current) {
				default:
					/*
					 * Rejoin files from listkeys_current[count_current - 1] and
					 *  files from listkeys_current[count_current - 2].
					 */
					if (listkeys_current[count_current - 1].file_first) {
						if (listkeys_current[count_current - 2].file_first) {
						  listkeys_current[count_current-2].file_last->file_next
							  = listkeys_current[count_current - 1].file_first;
						} else {
							listkeys_current[count_current - 2].file_first = 
							  listkeys_current[count_current - 1].file_first;
						}
						listkeys_current[count_current - 2].file_last = 
						  listkeys_current[count_current - 1].file_last;
					}
					count_current--;
					break;
				case 1:
					for (fc_file = listkeys_current[0].file_first; 
					  fc_file; fc_file = fc_file_next) {
						fc_file_next = fc_file->file_next;
						fc_file_free(fc_file);
					}
					free(listkeys_current);
					listkeys_current = NULL;
					count_current--;
					break;
				case 0:
					break;
				}
				break;
			default: /* Add a new current key to top of list. */
/* This doesn't wokr yet  ####################################### */
				/* First find if it is a key */
				for (fc_key = fc->keys; fc_key; fc_key = fc_key->key_next) {
					if (!strcmp(fc_key->key_name, key))
						break;
				}
				if (!fc_key) {
					printf("Can't find key %s for command %s\n", key, "lk");
					return(NOTOK);
				}
				if (count_current) {
					size = count_current * sizeof(struct fc_listkeys);
					if ((listkeys_current = (struct fc_listkeys *)realloc(
					  listkeys_current, size)) == NULL) {
						printf("Can't alloc space for command %s\n", "lk");
						return(NOTOK);
					}
					fc_file = listkeys_current[count_current - 1].file_first;
					listkeys_current[count_current - 1].file_first = NULL;
					listkeys_current[count_current].file_first = NULL;
					while(fc_file) {
						struct fc_files * fc_file_next = fc_file->file_next;

						for (i = 0; i < fc_file->filekey_count; i++) {
							if (fc_file->filekey_first[i].key->key_num ==
							  fc_key->key_num)
								break;
						}
						if (i == fc_file->filekey_count) {
							if (!(fc_file->file_next = 
							  listkeys_current[count_current-1].file_first)) {
								listkeys_current[count_current-1].file_last =
								  fc_file;
							}
							listkeys_current[count_current-1].file_first =
							  fc_file;
						} else {
							if (!(fc_file->file_next = 
							  listkeys_current[count_current].file_first)) {
								listkeys_current[count_current].file_last =
								  fc_file;
							}
							listkeys_current[count_current].file_first=fc_file;
						}
						fc_file = fc_file_next;
					}
/* This doesn't wokr yet  ####################################### */
				} else {
					if ((listkeys_current = (struct fc_listkeys *)malloc(
					  sizeof(struct fc_listkeys))) == NULL) {
						printf("Can't alloc space for command %s\n", "lk");
						return(NOTOK);
					}
					listkeys_current[0].key = fc_key;
					if (listkeys_current[0].file_count = fc_key->file_count) {
						if (listkeys_current[0].file_first = 
						  fc_file_alloc(fc_key->file_count)) {
							struct fc_filekeys * key_filekey;
							struct fc_files * key_file;

							key_file = fc_key->file_first;
							key_filekey =fc_key->filekey_first;
							fc_file = listkeys_current[0].file_first;
							do {
								fc_file->alloctype = OTHER_ALLOCED;
								fc_file->file_name = key_file->file_name;
								fc_file->filekey_first =key_file->filekey_first;
								fc_file->filekey_count =key_file->filekey_count;

								key_file = key_filekey->file_next;
								key_filekey = key_filekey->filekey_next;
							} while(fc_file = fc_file->file_next);
							fc_file->alloctype = OTHER_ALLOCED;
							fc_file->file_name = key_file->file_name;
							fc_file->filekey_first = key_file->filekey_first;
							fc_file->filekey_count = key_file->filekey_count;
							listkeys_current[0].file_last = fc_file;
						} else {
							printf("Can't alloc space for command %s\n", "lk");
							return(NOTOK);
						}
					} else {
						listkeys_current[0].file_last = NULL;
						listkeys_current[0].file_first = NULL;
					} 
				}
				count_current++;
				break;
			}
		} while (key = parse_get_key(data, &data));

		/* Now got the resulting file list and find all unlisted keys */
		for (fc_file = listkeys_current[count_current].file_first; fc_file; 
		  fc_file = fc_file->file_next) {

			for (i = 0; i < fc_file->filekey_count; i++) {
				struct fc_filekeys * fc_fk = fc_file->filekey_first + i;
				int j;

				for (j = 0; j < count_current; j++) {
					if (listkeys_current[j].key->key_num == fc_fk->key->key_num)
						break;
					
				}
				if (j >= count_current) {
					switch(fc_key_insert(fc_fk->key->key_name, &keys, &fc_key)){
					case OK:
						fc_key->alloctype = OTHER_ALLOCED;
						fc_key->key_name = fc_fk->key->key_name;
						fc_key->key_num = fc_fk->key->key_num;
						fc_key->file_first = NULL;
						break;
					case ENOMEM:
						printf("Can't alloc space for command %s\n", "lk");
					default:
						break;
					}
				}
			}
		}
		/* Display the keys and then free them. */
		for (fc_key = keys; fc_key; fc_key = keys) {
			printf("%s\n", fc_key->key_name);
			keys = fc_key->key_next;
			fc_key_free(fc_key);
		}
	} else {
		char ** strings;
		int i = 0;

		if (strings = (char **) malloc(sizeof(char *) * fc->keycount)) {
			for (fc_key = fc->keys; fc_key; fc_key = fc_key->key_next) {
				strings[i++] = fc_key->key_name;
			}
			fc_display(strings, fc->keycount);
			free(strings);
		} else {
			printf("Can't alloc space for command %s\n", "lk");
			return(NOTOK);
		}
	}
	return(OK);
}

/* ==========================================================================
 * fc_do_rk()
 *
 * For now, only allow deleting one key at a time.
 */
void fc_do_rk(struct fc * fc, char * data)
{
	struct fc_keys ** fc_keyp, *fc_key;
	int result, in_use, i;
	char * key;

	while (key = parse_get_key(data, &data)) {
		if (parse_key_is_special(key)) 
			continue;
		
		for (fc_keyp = &fc->keys; *fc_keyp; fc_keyp = &((*fc_keyp)->key_next)) {
			if ((result = strcmp(key, (*fc_keyp)->key_name)) == 0) {
				/* Key cannot be in use or have any files associated */
				for (in_use = i = 0; i < fc->count_current; i++) {
					if (fc->listkeys_current[i].key->key_num == 
					  (*fc_keyp)->key_num) {
						in_use++;
						break;
					}
				}
				if (in_use || (*fc_keyp)->file_count) {
					printf("Key %s in use for command %s\n", key, "rk");
					break;
				}
				fc_key = *fc_keyp;
				*fc_keyp = fc_key->key_next;
				if (fc_key->alloctype == IND_ALLOCED) 
					free(fc_key->key_name);
				fc_key_free(fc_key);

				for (fc_key = *fc_keyp; fc_key; fc_key = fc_key->key_next) {
					fc_key->key_num--;
				}
				fc->keycount--;
				break;
			}
			if (result < 0) {
				printf("Can't find key %s for command %s\n", key, "rk");
				break;
			}
		}
	}
}

/* ==========================================================================
 * fc_do_alk()
 */
int fc_do_alk(struct fc * fc, char * data)
{
	struct fc_files * fc_file, * fc_file_next;
	struct fc_keys * fc_key;
	int count, size, ret, i, j;
	char * key;

	while (key = parse_get_key(data, &data)) {
		/* Must reset count each iteration */
		count = fc->count_current;		

		switch (parse_key_is_special(key)) {
		case 3: /* Clear current listkeys */
			if (fc->count_current) {
				for (i = 0; i < count; i++) {
					for (fc_file = fc->listkeys_current[i].file_first; 
					  fc_file; fc_file = fc_file_next) {
						fc_file_next = fc_file->file_next;
						fc_file_free(fc_file);
					}
				}
				free (fc->listkeys_current);
				fc->listkeys_current = NULL;
				fc->count_current = 0;
			}
		case 1: /* No effect */
			break;
		case 2: /* Remove the top current key */
			switch (fc->count_current) {
			default:
				/*
				 * Rejoin files from listkeys_current[count_current - 1] and
				 *  files from listkeys_current[count_current - 2].
				 */
				if (fc->listkeys_current[count - 1].file_first) {
					if (fc->listkeys_current[count - 2].file_first) {
						fc->listkeys_current[count - 2].file_last->file_next = 
						  fc->listkeys_current[count - 1].file_first;
					} else {
						fc->listkeys_current[count - 2].file_first = 
						  fc->listkeys_current[count - 1].file_first;
					}
					fc->listkeys_current[count - 2].file_last = 
					  fc->listkeys_current[count - 1].file_last;
				}
				fc->listkeys_current[count - 2].file_count +=
				  fc->listkeys_current[count - 1].file_count;
				fc->count_current--;
				break;
			case 1:
				for (fc_file = fc->listkeys_current[0].file_first; 
				  fc_file; fc_file = fc_file_next) {
					fc_file_next = fc_file->file_next;
					fc_file_free(fc_file);
				}
				free (fc->listkeys_current);
				fc->listkeys_current = NULL;
				fc->count_current--;
			case 0:
				break;
			}
			break;
		default: /* Add a new current key to top of list */
			/* First find if it is a key */
			for (fc_key = fc->keys; fc_key; fc_key = fc_key->key_next) {
				if (!strcmp(fc_key->key_name, key))
					break;
			}
			if (!fc_key) {
				printf("Can't find key %s for command %s\n", key, "ack");
				return(NOTOK);
			}
			if (count) {
				size = (count + 1) * sizeof(struct fc_listkeys);
				if ((fc->listkeys_current = (struct fc_listkeys *)realloc(
				  fc->listkeys_current, size)) == NULL) {
					printf("Can't alloc space for command %s\n", "ack");
					return(NOTOK);
				}
				fc_file = fc->listkeys_current[count - 1].file_first;
				fc->listkeys_current[count - 1].file_first = NULL;
				fc->listkeys_current[count - 1].file_last = NULL;
				fc->listkeys_current[count].file_first = NULL;
				fc->listkeys_current[count].file_last = NULL;
				j = 0;
				while(fc_file) {
					fc_file_next = fc_file->file_next;

					for (i = 0; i < fc_file->filekey_count; i++) {
						if (fc_file->filekey_first[i].key->key_num ==
						  fc_key->key_num)
							break;
					}
					if (i >= fc_file->filekey_count) {
						if (!(fc_file->file_next = 
						  fc->listkeys_current[count - 1].file_first)) {
							fc->listkeys_current[count - 1].file_last = fc_file;
						}
						fc->listkeys_current[count - 1].file_first = fc_file;
					} else {
						j++;
						if (!(fc_file->file_next = 
						  fc->listkeys_current[count].file_first)) {
							fc->listkeys_current[count].file_last = fc_file;
						}
						fc->listkeys_current[count].file_first = fc_file;
					}
					fc_file = fc_file_next;
				}
				fc->listkeys_current[count].key = fc_key;
				fc->listkeys_current[count].file_count = j;
				fc->listkeys_current[count - 1].file_count -= j;
			} else {
				if ((fc->listkeys_current = (struct fc_listkeys *)malloc(
				  sizeof(struct fc_listkeys))) == NULL) {
					printf("Can't alloc space for command %s\n", "lk");
					return(NOTOK);
				}
				if ((fc->listkeys_current->file_count = fc_key->file_count) && 
				  (fc->listkeys_current[0].file_first = 
				  fc_file_alloc(fc_key->file_count))) {
					struct fc_files * key_file = fc_key->file_first;
					struct fc_filekeys * key_filekey = fc_key->filekey_first;

					for (fc_file = fc->listkeys_current[0].file_first; 
					  fc_file->file_next; fc_file = fc_file->file_next) {
						fc_file->filekey_count = key_file->filekey_count;
						fc_file->filekey_first = key_file->filekey_first;
						fc_file->file_name = key_file->file_name;
						fc_file->alloctype = OTHER_ALLOCED;

						key_file = key_filekey->file_next;
						key_filekey = key_filekey->filekey_next;
					} 
					fc_file->filekey_count = key_file->filekey_count;
					fc_file->filekey_first = key_file->filekey_first;
					fc_file->file_name = key_file->file_name;
					fc_file->alloctype = OTHER_ALLOCED;
					fc->listkeys_current->file_last = fc_file;
				} else {
					if (fc->listkeys_current->file_count == 0) {
						fc->listkeys_current->file_first = NULL;
						fc->listkeys_current->file_last = NULL;
					} else {
					    printf("Can't alloc space for command %s\n", "ack");
					    free (fc->listkeys_current);
					    return(NOTOK);
					}
				}
				fc->listkeys_current->key = fc_key;
			}
			fc->count_current++;
			break;
		}
	} 
	return(OK);
}

/* ==========================================================================
 * fc_do_llk()
 */
int fc_do_llk(struct fc * fc, char * key)
{
	int i;

	for (i = 0; i < fc->count_current; i++) {
		printf("%s ", fc->listkeys_current[i].key->key_name);
	}
	printf("\n");
	return(OK);
}

/* ==========================================================================
 * fc_do_rlk()
 */
void fc_do_rlk(struct fc * fcp, char * key)
{
	printf("The remove current key command is not implemented yet\n");
}

