/*	$OpenBSD: files.c,v 1.5 1997/06/30 05:36:16 millert Exp $	*/

/*-
 * Copyright (c) 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Peter McIlroy.
 *
 * 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 the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
 */

#ifndef lint
#if 0
static char sccsid[] = "@(#)files.c	8.1 (Berkeley) 6/6/93";
#else
static char rcsid[] = "$OpenBSD: files.c,v 1.5 1997/06/30 05:36:16 millert Exp $";
#endif
#endif /* not lint */

#include "sort.h"
#include "fsort.h"

#include <string.h>
#include "pathnames.h"


/*
 * xfopen() is a fopen() wrapper that emulates /dev/stdin and /dev/stdout
 * on systems (Linux 2.0.28) that either don't have them or implement them
 * incorrectly.
 */
FILE *
xfopen(char *file, char *mode)
{
#ifdef LINUX
    if (strcmp(file, _PATH_STDIN) != 0)
	return fopen(file, mode);
    else if (strchr(mode, 'r') != 0)
	return stdin;
    errno = EACCES;
    return 0;
#else
    return fopen(file, mode);
#endif
} /* xfopen */

/*
 * this is the subroutine for file management for fsort().
 * It keeps the buffers for all temporary files.
 */
int
getnext(binno, infl0, nfiles, pos, end, dummy)
	int binno;
	union f_handle infl0;
	int nfiles;
	register RECHEADER *pos;
	register u_char *end;
	struct field *dummy;
{
	register int i;
	register u_char *hp;
	static int nleft = 0;
	static int cnt = 0, flag = -1;
	static u_char maxb = 0;
	static FILE *fp;

	if (nleft == 0) {
		if (binno < 0)	/* reset files. */ {
			for (i = 0; i < nfiles; i++) {
				rewind(fstack[infl0.top + i].fp);
				fstack[infl0.top + i].max_o = 0;
			}
			flag = -1;
			nleft = cnt = 0;
			return (-1);
		}
		maxb = fstack[infl0.top].maxb;
		for (; nleft == 0; cnt++) {
			if (cnt >= nfiles) {
				cnt = 0;
				return (EOF);
			}
			fp = fstack[infl0.top + cnt].fp;
			hp = (u_char *) &nleft;
			for (i = sizeof(TRECHEADER); i; --i)
				*hp++ = getc(fp);
			if (binno < maxb)
				fstack[infl0.top+cnt].max_o
					+= sizeof(nleft) + nleft;
			else if (binno == maxb) {
				if (binno != fstack[infl0.top].lastb) {
					fseek(fp, fstack[infl0.top+
						cnt].max_o, SEEK_SET);
					fread(&nleft, sizeof(nleft), 1, fp);
				}
				if (nleft == 0)
					fclose(fp);
			} else if (binno == maxb + 1) {		/* skip a bin */
				fseek(fp, nleft, SEEK_CUR);
				fread(&nleft, sizeof(nleft), 1, fp);
				flag = cnt;
			}
		}
	}
	if ((u_char *) pos > end - sizeof(TRECHEADER))
		return (BUFFEND);
	hp = (u_char *) pos;
	for (i = sizeof(TRECHEADER); i ; --i)
		*hp++ = (u_char) getc(fp);
	if (end - pos->data < pos->length) {
		for (i = sizeof(TRECHEADER); i ;  i--)
			ungetc(*--hp, fp);
		return (BUFFEND);
	}
	fread(pos->data, pos->length, 1, fp);
	nleft -= pos->length + sizeof(TRECHEADER);
	if (nleft == 0 && binno == fstack[infl0.top].maxb)
		fclose(fp);
	return (0);
}

/*
 * this is called when there is no special key. It's only called
 * in the first fsort pass.
 */
int
makeline(flno, filelist, nfiles, buffer, bufend, dummy2)
	int flno;
	union f_handle filelist;
	int nfiles;
	RECHEADER *buffer;
	u_char *bufend;
	struct field *dummy2;
{
	static char *opos;
	register char *end, *pos;
	static int fileno = 0, overflow = 0;
	static FILE *fp = 0;
	register int c;

	pos = (char *) buffer->data;
	end = min((char *) bufend, pos + MAXLLEN);
	if (overflow) {
		memmove(pos, opos, bufend - (u_char *) opos);
		pos += ((char *) bufend - opos);
		overflow = 0;
	}
	for (;;) {
		if (flno >= 0 && (fp = fstack[flno].fp) == NULL)
			return (EOF);
		else if (fp == 0) {
			if (fileno  >= nfiles)
				return (EOF);
			if (!(fp = xfopen(filelist.names[fileno], "r")))
				err(2, filelist.names[fileno]);
			fileno++;
		}
		while ((pos < end) && ((c = getc(fp)) != EOF)) {
			if ((*pos++ = c) == REC_D) {
				buffer->offset = 0;
				buffer->length = pos - (char *) buffer->data;
				return (0);
			}
		}
		if (pos >= end && end == (char *) bufend) {
			if ((char *) buffer->data < end) {
				overflow = 1;
				opos = (char *) buffer->data;
			}
			return (BUFFEND);
		} else if (c == EOF) {
			if (buffer->data != (u_char *) pos) {
#if 0
				warnx("last character not record delimiter");
#endif
				*pos++ = REC_D;
				buffer->offset = 0;
				buffer->length = pos - (char *) buffer->data;
				return (0);
			}
			FCLOSE(fp);
			fp = 0;
			if (flno >= 0)
				fstack[flno].fp = 0;
		} else {
			buffer->data[100] = '\000';
			warnx("line too long: ignoring %s...", buffer->data);
		}
	}
}

/*
 * This generates keys. It's only called in the first fsort pass
 */
int
makekey(flno, filelist, nfiles, buffer, bufend, ftbl)
	int flno, nfiles;
	union f_handle filelist;
	RECHEADER *buffer;
	u_char *bufend;
	struct field *ftbl;
{
	static int (*get)();
	static int fileno = 0;
	static FILE *dbdesc = 0;
	static DBT dbkey[1], line[1];
	static int overflow = 0;
	int c;

	if (overflow) {
		overflow = 0;
		enterkey(buffer, line, bufend - (u_char *) buffer, ftbl);
		return (0);
	}
	for (;;) {
		if (flno >= 0) {
			get = seq;
			if (!(dbdesc = fstack[flno].fp))
				return (EOF);
		} else if (!dbdesc) {
			if (fileno  >= nfiles)
				return (EOF);
			dbdesc = xfopen(filelist.names[fileno], "r");
			if (!dbdesc)
				err(2, filelist.names[fileno]);
			fileno++;
			get = seq;
		}
		if (!(c = get(dbdesc, line, dbkey))) {
			if ((signed)line->size > bufend - buffer->data)
				overflow = 1;
			else
				overflow = enterkey(buffer, line,
				    bufend - (u_char *) buffer, ftbl);
			if (overflow)
				return (BUFFEND);
			else
				return (0);
		}
		if (c == EOF) {
			FCLOSE(dbdesc);
			dbdesc = 0;
			if (flno >= 0)
				fstack[flno].fp = 0;
		} else {
			((char *) line->data)[60] = '\000';
			warnx("line too long: ignoring %.100s...",
			    (char *)line->data);
		}
	}
}

/*
 * get a key/line pair from fp
 */
int
seq(fp, line, key)
	FILE *fp;
	DBT *line;
	DBT *key;
{
	static char *buf, flag = 1;
	register char *end, *pos;
	register int c;

	if (flag) {
		flag = 0;
		buf = (char *) linebuf;
		end = buf + MAXLLEN;
		line->data = buf;
	}
	pos = buf;
	while ((c = getc(fp)) != EOF) {
		if ((*pos++ = c) == REC_D) {
			line->size = pos - buf;
			return (0);
		}
		if (pos == end) {
			line->size = MAXLLEN;
			*--pos = REC_D;
			while ((c = getc(fp)) != EOF) {
				if (c == REC_D)
					return (BUFFEND);
			}
		}
	}
	if (pos != buf) {
#if 0
		warnx("last character not record delimiter");
#endif
		*pos++ = REC_D;
		line->size = pos - buf;
		return (0);
	} else
		return (EOF);
}

/*
 * write a key/line pair to a temporary file
 */
void
putrec(rec, fp)
	register RECHEADER *rec;
	register FILE *fp;
{
	EWRITE(rec, 1, rec->length + sizeof(TRECHEADER), fp);
}

/*
 * write a line to output
 */
void
putline(rec, fp)
	register RECHEADER *rec;
	register FILE *fp;
{
	EWRITE(rec->data+rec->offset, 1, rec->length - rec->offset, fp);
}

/*
 * get a record from a temporary file. (Used by merge sort.)
 */
int
geteasy(flno, filelist, nfiles, rec, end, dummy2)
	int flno, nfiles;
	union f_handle filelist;
	register RECHEADER *rec;
	register u_char *end;
	struct field *dummy2;
{
	int i;
	FILE *fp;

	fp = fstack[flno].fp;
	if ((u_char *) rec > end - sizeof(TRECHEADER))
		return (BUFFEND);
	if (!fread(rec, 1, sizeof(TRECHEADER), fp)) {
		fclose(fp);
		fstack[flno].fp = 0;
		return (EOF);
	}
	if (end - rec->data < rec->length) {
		for (i = sizeof(TRECHEADER) - 1; i >= 0;  i--)
			ungetc(*((char *) rec + i), fp);
		return (BUFFEND);
	}
	fread(rec->data, rec->length, 1, fp);
	return (0);
}
