/*
 *	Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *	This will be free software, but only when it is finished.
 */

/* LINTLIBRARY */

#include "hostenv.h"
#include "mailer.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/file.h>
#include "search.h"
#include "io.h"

/*
 * Linear search of a file for keyword-value pairs.
 */

struct conscell *
search_seq(sip)
	struct search_info *sip;
{
	FILE *fp;
	register unsigned char *cp, *s;
	struct conscell *tmp;
	struct spblk *spl;
	int retry, symid;
	char buf[BUFSIZ];
	extern int deferit, cistrcmp();
	extern void v_set();

	if (sip->file == NULL)
		return NULL;
	retry = 0;
	symid = symbol((u_char *)sip->file);
	spl = sp_lookup(symid, spt_files);
	if (spl == NULL || (fp = (FILE *)spl->data) == NULL) {
reopen:
		fp = fopen(sip->file, "r");
		if (fp == NULL) {
			++deferit;
			v_set(DEFER, DEFER_IO_ERROR);
			fprintf(stderr, "search_seq: cannot open %s!\n",
					sip->file);
			return NULL;
		}
		if (spl == NULL)
			sp_install(symid, (u_char *)fp, O_RDONLY, spt_files);
		else
			spl->data = (u_char *)fp;
	}
	(void) fseek(fp, 0L, 0);
	while ((s = (u_char *)fgets(buf, sizeof buf, fp)) != NULL) {
		buf[sizeof buf - 1] = '\0';
		for (cp = (u_char *)buf; *cp; ++cp)
			if (!isascii(*cp) || isspace(*cp))
				break;
		if (*cp == '\0')
			*(cp+1) = '\0';
		else
			*cp = '\0';
		if (cistrcmp((char *)(sip->key), buf) == 0) {
			for (++cp; *cp; ++cp)
				if (isascii(*cp) && !isspace(*cp))
					break;
			for (s = cp; *s != '\0'; ++s)
				if (!isascii(*s) || isspace(*s))
					break;
			return newstring((u_char *)strnsave((char *)cp, s - cp));
		}
	}
	if (!retry && ferror(fp)) {
		(void) fclose(fp);
		++retry;
		goto reopen;
	}

	return NULL;
}

/*
 * Flush buffered information from this database, close any file descriptors.
 */

void
close_seq(sip)
	struct search_info *sip;
{
	FILE *fp;
	struct spblk *spl;
	int symid;

	if (sip->file == NULL)
		return;
	symid = symbol((u_char *)sip->file);
	spl = sp_lookup(symid, spt_modcheck);
	if (spl != NULL)
		sp_delete(spl, spt_modcheck);
	spl = sp_lookup(symid, spt_files);
	if (spl == NULL || (fp = (FILE *)spl->data) == NULL)
		return;	/* nothing to flush */
	(void) fclose(fp);
	(void) sp_install(symid, (u_char *)NULL, 0, spt_files);
}

FILE *
open_seq(sip, mode)
	struct search_info *sip;
	char *mode;
{
	FILE *fp;
	struct spblk *spl;
	int symid, imode;
	extern int deferit;
	extern void v_set();

	if (sip->file == NULL)
		return NULL;
	symid = symbol((u_char *)sip->file);
	spl = sp_lookup(symid, spt_files);
	if (spl != NULL
	    && (*mode == 'w' || *mode == 'a'
		      || (*mode == 'r' && *(mode+1) == '+'))
	    && spl->mark != O_RDWR) {
		close_seq(sip);
		imode = O_RDWR;
	} else
		imode = O_RDONLY;
	if (spl == NULL || (fp = (FILE *)spl->data) == NULL) {
		fp = fopen(sip->file, mode);
		if (fp == NULL) {
			++deferit;
			v_set(DEFER, DEFER_IO_ERROR);
			fprintf(stderr,
				"add_seq: cannot open %s mode \"%s\"!\n",
				sip->file, mode);
			return NULL;
		}
		if (spl == NULL)
			sp_install(symid, (u_char *)fp, imode, spt_files);
		else
			spl->data = (u_char *)fp;
	}
	return fp;
}

/*
 * Add the indicated key/value pair to the list.
 */

int
add_seq(sip, value)
	struct search_info *sip;
	char *value;
{
	FILE *fp;

	if ((fp = open_seq(sip, "r+")) == NULL)
		return EOF;
	(void) fseek(fp, 0L, 2);
	if (value == NULL || *value == '\0')
		fprintf(fp, "%s\n", sip->key);
	else
		fprintf(fp, "%s\t%s\n", sip->key, value);
	return fflush(fp);
}

/*
 * Print the database.  This is equivalent to listing the file and so it
 * can be used by the other text-reading database types, e.g. search_bin().
 */

void
print_seq(sip, outfp)
	struct search_info *sip;
	FILE *outfp;
{
	FILE *fp;
	int n;
	char buf[BUFSIZ];

	if ((fp = open_seq(sip, "r")) == NULL)
		return;

	(void) fseek(fp, 0L, 0);
	while ((n = fread(buf, 1, sizeof buf, fp)) > 0)
		fwrite(buf, 1, n, outfp);
	(void) fflush(outfp);
}

void
owner_seq(sip, outfp)
	struct search_info *sip;
	FILE *outfp;
{
	FILE *fp;
	struct stat stbuf;

	if (sip->file == NULL || (fp = open_seq(sip, "r")) == NULL)
		return;

	if (fstat(fileno(fp), &stbuf) < 0) {
		(void) fprintf(stderr, "owner_seq: cannot fstat(\"%s\")!\n",
				sip->file);
		return;
	}
	(void) fprintf(outfp, "%d\n", stbuf.st_uid);
	(void) fflush(outfp);
}

int
modp_seq(sip)
	struct search_info *sip;
{
	FILE *fp;
	struct stat stbuf;
	struct spblk *spl;
	int symid, rval;

	if (sip->file == NULL || (fp = open_seq(sip, "r")) == NULL)
		return 0;

	if (fstat(fileno(fp), &stbuf) < 0) {
		fprintf(stderr, "modp_seq: cannot fstat(\"%s\")!\n",
				sip->file);
		return 0;
	}

	symid = symbol((u_char *)sip->file);
	spl = sp_lookup(symid, spt_modcheck);
	if (spl != NULL) {
		rval = stbuf.st_mtime != (time_t)spl->data
			|| stbuf.st_nlink != (int)spl->mark;
	} else
		rval = 0;
	sp_install(symid, (u_char *)stbuf.st_mtime,
			  stbuf.st_nlink, spt_modcheck);
	return rval;
}
