/* Copyright 1990, AT&T Bell Labs */
#include "fsort.h"
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGPIPE 13
#define SIGTERM 15

extern int getpid(void);
extern int access(char*, int);
extern int unlink(char*);
extern int stat(const char*, struct stat *);
extern int cgets(char*, int, FILE*);

FILE *
fileopen(char *name, char *mode)
{
	FILE *f;
	if(strcmp(name,"-") == 0)
		if(strcmp(mode, "r") == 0)
			f = stdin;
		else {
			setbuf(stdout,malloc(BUFSIZ));
			f = stdout;
		}
	else {
		if(strcmp(mode, "w") == 0 &&
		   strcmp(name, oname) == 0 &&
		   overwrite(0))
			setsigs(SIG_IGN);
		f = fopen(name, mode);
	}
	if(f == 0)
		fatal("can't open", name, 0);
	return f;
}

void
fileclose(FILE *f, char *name)
{
	if(fclose(f)==EOF && name!=0)
		fatal("error on", name, 0);
}

/* file name strings accumulate as garbage */

char *
filename(int number)
{
	char name[50];
	char *s;
	int i;
	for(i=0; (s=tname[i])!=0; i++)
		if(access(s, 03) != -1)
			break;
	if(s == 0)
		fatal("no accessible temp directory", "", 0);
	sprintf(name, "%s/stm%.5d.%.4d", s, getpid(), number);
	s = malloc(strlen(name) + 1);
	if(s == 0)
		fatal("out of space", "", 0);
	strcpy(s, name);
	return s;
}

/* if there is enough room in record r, getline puts
   a line of data there and returns 0; otherwise it
   returns a pointer to a new record.  The record
   may be grown in stages; intermediate stages can
   be discarded, but the original cannot. */

static struct rec *
newrec(struct rec *r, struct rec *retval)
{
	int n = (uchar*)r->next - data(r);
	int len = (uchar*)r->next - (uchar*)r;
	struct rec *new = (struct rec*)malloc(len + n);
	if(new == 0)
		fatal("no space for record", "", 0);
	memmove(new, r, len);
	new->next = (struct rec*)((uchar*)new + len + n);
	if(retval)
		free(retval);
	return new;
}

struct rec*
getline(struct rec *r, FILE *f)
{
	int n = 0;
	int m;
	uchar *cp, *dp;
	struct rec *retval = 0;

	if(feof(f))		/* in case newline was appended */
		return ENDFILE;
	for(;;) {		/* usually executed once */
		dp = data(r);
		cp = dp + n;
		m = (uchar*)r->next - cp;
		if(m <= 1) 
			retval = r = newrec(r, retval);
		else {
			m = cgets((char*)cp, m, f);
			if(m == 0) {
				if(n == 0)
					return ENDFILE;
				warn("newline appended", "", 0);
				break;
			}
			n += m;
			if(dp[n-1] == '\n') {
				n--;
				break;
			}
		}
	}

	r->dlen = n;
	if(n > MAXREC)
		fatal("monster record", "", 0);
	if(!keyed) {
		r->klen = 0;	/* hygiene */
		return retval;
	}
	while((n = fieldcode(data(r),key(r),
			     r->dlen,(uchar*)r->next)) < 0)
		retval = r = newrec(r, retval);	/* rare event */
	if(n > MAXREC)
		fatal("monster key", "", 0);
	r->klen = n;
	return retval;
}

static char *level = "warning";

void
warn(char *m, char *s, int l)
{
	fprintf(stderr, "sort: %s: %s %.*s\n",
		level, m, l==0?strlen(s):l, s);
}

void
fatal(char *m, char *s, int l)
{
	level = "error";
	warn(m, s, l);
	if(errno)
		perror("");
	cleanup(1);
}

int
overwrite(int j)
{
	struct stat sb1, sb2;
	if(strcmp(oname, "-") == 0)
		return 0;
	if(stat(oname, &sb1) == -1)
		return 0;
	for( ; j<nfiles; j++) {
		if(strcmp(files[j], "-") == 0)
			continue;
		if(stat(files[j], &sb2) == -1)
			fatal("cannot locate", files[j], 0);
		if(sb1.st_dev==sb2.st_dev && sb1.st_ino==sb2.st_ino)
			return 1;
	}
	return 0;
}

static int siglist[] = { SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM };

void
setsigs(void(*f)(int))
{
	int i;
	for(i=0; i<sizeof(siglist)/sizeof(*siglist); i++)
		if(signal(siglist[i], f) == SIG_IGN)
			signal(siglist[i], SIG_IGN);
}

void
cleanup(int i)
{
	char *name;
	setsigs(SIG_IGN);
	while(--nextfile >= 0) {
		name = filename(nextfile);
		unlink(name);
		free(name);
	}
	exit(i);
}
