#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/file.h>

#include "AVLTree.h"
#include "error.h"
#include "conffile.h"
#include "symbol.h"
#include "status.h"
#include "keepers.h"

#define READBUF 4096

AVLTree *keepers = NULL, *nokeepers = NULL;
static int keeperfd = -1;

void openkeepers(int readonly) {
	if(readonly) {
		keeperfd = open(KeeperFile, O_RDONLY);
		if(keeperfd<0 && errno != ENOENT)
			perror_exit(ERROR_CONFIG, KeeperFile);
		return;
	}

	keeperfd = open(KeeperFile, O_RDWR|O_CREAT, 0666);
	if(keeperfd < 0)
		perror_exit(ERROR_CONFIG, KeeperFile);

	if(flock(keeperfd, LOCK_EX|LOCK_NB)) {
		if(getenv("DEBFOSTER_LOCK_SOFTFAIL"))
			exit(0);
		fprintf(stderr, "debfoster is unable to acquire a lock on the keeper file.\nAnother debfoster process probably has it.\n");
		perror_exit(ERROR_USER, KeeperFile);
	}

	if(setenv("DEBFOSTER_LOCK_SOFTFAIL", "", 0))
		perror_exit(ERROR_SYSTEM, "setenv()");

}

void closekeepers(void) {
	if(keeperfd < 0)
		return;

	flock(keeperfd, LOCK_UN);
	close(keeperfd);
	keeperfd = -1;
}

void writekeepers(void) {
	FILE *f;
	AVLNode *c;

	if(keeperfd < 0)
		return;

	ftruncate(keeperfd, 0);
	f = fdopen(dup(keeperfd), "w");
	if(!f)
		perror_exit(ERROR_SYSTEM, KeeperFile);
	rewind(f);

	for(c = keepers->head; c; c = c->next)
		fprintf(f, "%s\n", (char *)c->item);
	for(c = nokeepers->head; c; c = c->next)
		fprintf(f, "-%s\n", (char *)c->item);

	fclose(f);
}

void readkeepers(void) {
	FILE *f;
	char buf[READBUF], *s;
	symbol_t name;
	int keep;

	if(keeperfd < 0)
		return;

	f = fdopen(dup(keeperfd), "r");
	if(!f)
		perror_exit(ERROR_SYSTEM, KeeperFile);
	rewind(f);

	while(fgets(buf, READBUF, f)) {
		s = chop(buf);

		switch(*s) {
			case '-':
				s++;
				keep = 0;
			break;
			case '+':
				s++;
			default:
				keep = 1;
		}
		if (*s) {
			name = symbol(s);
			if(keep)
				AVLInsert(keepers, name);
			else if (NegativeKeepers && !AVLSearch(keepers, name)) /* sanity check */
				AVLInsert(nokeepers, name);
		}
	}

	fclose(f);
}
