/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1996-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                   Phong Vo <kpv@research.att.com>                    *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 *
 * rskey coding for recsort
 *
 * Doug McIlroy did the hard part here
 * (and with regression tests too)
 */

#include "rskeyhdr.h"

#include <hashpart.h>

#if _sys_resource && _lib_getrlimit

#include <times.h>
#include <sys/resource.h>

static size_t
datasize(void)
{
	struct rlimit	rlim;

	getrlimit(RLIMIT_DATA, &rlim);
	return rlim.rlim_cur;
}

#else

#define datasize()	(size_t)(128*1024*1024)

#endif

/*
 * Canonicalize the number string pointed to by dp, of length
 * len.  Put the result in kp.
 *
 * A field of length zero, or all blank, is regarded as 0.
 * Over/underflow is rendered as huge or zero and properly signed.
 * It happens 1e+-1022.
 *
 * Canonicalized strings may be compared as strings of unsigned
 * chars.  For good measure, a canonical string has no zero bytes.
 *
 * Syntax: optionally signed floating point, with optional
 * leading spaces.  A syntax deviation ends the number.
 *
 * Form of output: packed in 4-bit nibbles.  First
 * 3 nibbles count the number N of significant digits
 * before the decimal point.  The quantity actually stored
 * is 2048+sign(x)*(N+1024).  Further nibbles contain
 * 1 decimal digit d each, stored as d+2 if x is positive
 * and as 10-d if x is negative.  Leading and trailing
 * zeros are stripped, and a trailing "digit" d = -1 
 * is appended.  (The trailing digit handled like all others,
 * so encodes as 1 or 0xb according to the sign of x.)
 * An odd number of nibbles is padded with zero.
 *
 * Buglet: overflow is reported if output is exactly filled.
 */

#define encode(x)	(neg?(10-(x)):((x)+2))
#define putdig(x)	(nib?(*dig=encode(x)<<4,nib=0):(*dig++|=encode(x),nib=1))

static int
#if __STD_C
key_n_code(Rskey_t* kp, Field_t* f, unsigned char* dp, unsigned char* cp, int len)
#else
key_n_code(kp, f, dp, cp, len)
Rskey_t*	kp;
Field_t*	f;
unsigned char*	dp;
unsigned char*	cp;
int		len;
#endif
{
	unsigned char*	dig = cp + 1;	/* byte for next digit */
	int		nib = 0;	/* high nibble 1, low nibble 0 */
	unsigned char*	xp = dp;
	unsigned char*	ep = xp + len;	/* end pointer */
	unsigned char*	trans = f->trans;
	int		zeros = 0;	/* count zeros seen but not installed */
	int		sigdig = 1024;
	int		neg = f->rflag;	/* 0 for +, 1 for - */
	int		decimal = 0;
	int		n;
	int		inv;

	cp[1] = 0;

	/*
	 * eat blanks
	 */

	while (xp < ep && blank(trans[*xp])) xp++;

	/*
	 * eat sign
	 */

	if (xp < ep)
		switch (trans[*xp])
		{
		case '-':
			neg ^= 1;
			/*FALLTHROUGH*/
		case '+':
			xp++;
			break;
		}

	/*
	 * eat leading zeros
	 */

	while (xp < ep && trans[*xp] == '0') xp++;
	if (xp < ep && trans[*xp] == '.')
	{
		decimal++;
		for (xp++; xp < ep && trans[*xp] == '0'; xp++)
			sigdig--;
	}
	if (xp >= ep || trans[*xp] > '9' || trans[*xp] < '0')
	{
		/*
		 * no significant digit
		 */

		sigdig = 0;
		neg = 0;
		goto retzero;
	}
	for (; xp < ep; xp++)
	{
		switch (trans[*xp])
		{
		case '.':
			if (decimal)
				goto out;
			decimal++;
			continue;
		case '0':
			zeros++;
			if (!decimal)
				sigdig++;
			continue;
		case '1': case '2': case '3': case '4': case '5':
		case '6': case '7': case '8': case '9':
			for (; zeros > 0; zeros--)
				putdig(0);
			n = trans[*xp] - '0';
			putdig(n);
			if (!decimal)
				sigdig++;
			continue;
		case 'e':
		case 'E':
			if (f->flag != 'g')
				goto out;
			inv = 1;
			if (xp < ep) switch(trans[*++xp])
			{
			case '-':
				inv = -1;
				/*FALLTHROUGH*/
			case '+':
				xp++;
				break;
			}
			if (xp >= ep || trans[*xp] > '9' || trans[*xp] < '0')
				goto out;
			for (n = 0; xp < ep; xp++)
			{
				int	c = trans[*xp];

				if (c < '0' || c > '9')
					break;
				if ((n = 10 * n + c - '0') >= 0)
					continue;
				sigdig = 2047 * inv;
				goto out;
			}
			sigdig += n * inv;
			goto out;
		default:
			goto out;
		}
	}
 out:
	if (sigdig < 0 || sigdig >= 2047)
	{
		sigdig = sigdig < 0 ? 0 : 2047;
		if (kp->keydisc->errorf)
			(*kp->keydisc->errorf)(kp, kp->keydisc, 1, "%-.*s: numeric field overflow", len, dp);
		dig = cp + 1;
		*dig = 0;
		nib = 0;
	}
 retzero:
	if (neg) sigdig = 2048 - sigdig;
	else sigdig = 2048 + sigdig;
	cp[0] = sigdig >> 4;
	cp[1] |= sigdig << 4;
	putdig(-1);
	return dig - cp + 1 - nib;
}

/*
 * packed decimal (bcd)
 */

static int
#if __STD_C
key_p_code(Rskey_t* kp, Field_t* f, unsigned char* dp, unsigned char* cp, int len)
#else
key_p_code(kp, f, dp, cp, len)
Rskey_t*	kp;
Field_t*	f;
unsigned char*	dp;
unsigned char*	cp;
int		len;
#endif
{
	unsigned char*	dig = cp + 1;	/* byte for next digit */
	int		nib = 0;	/* high nibble 1, low nibble 0 */
	unsigned char*	xp = dp;
	unsigned char*	ep = xp + len;	/* end pointer */
	unsigned char*	trans = f->trans;
	int		sigdig = 1024;
	int		neg = f->rflag;	/* 0 for +, 1 for - */
	int		n;
	int		c;

	cp[1] = 0;

	/*
	 * sign
	 */

	if ((trans[*(ep - 1)] & 0xF) == 0xD)
		neg ^= 1;
	while (xp < ep)
	{
		c = trans[*xp++];
		n = (c >> 4) & 0xF;
		putdig(n);
		sigdig++;
		n = c & 0xF;
		if (n > 0x9)
			break;
		putdig(n);
		sigdig++;
	}
	if (sigdig >= 2047)
	{
		sigdig = 2047;
		if (kp->keydisc->errorf)
			(*kp->keydisc->errorf)(kp, kp->keydisc, 1, "%-.*s: numeric field overflow", dp);
		dig = cp + 1;
		*dig = 0;
		nib = 0;
	}
	if (neg) sigdig = 2048 - sigdig;
	else sigdig = 2048 + sigdig;
	cp[0] = sigdig >> 4;
	cp[1] |= sigdig << 4;
	putdig(-1);
	return dig - cp + 1 - nib;
}

/*
 * zoned decimal
 */

static int
#if __STD_C
key_z_code(Rskey_t* kp, Field_t* f, unsigned char* dp, unsigned char* cp, int len)
#else
key_z_code(kp, f, dp, cp, len)
Rskey_t*	kp;
Field_t*	f;
unsigned char*	dp;
unsigned char*	cp;
int		len;
#endif
{
	unsigned char*	dig = cp + 1;	/* byte for next digit */
	int		nib = 0;	/* high nibble 1, low nibble 0 */
	unsigned char*	xp = dp;
	unsigned char*	ep = xp + len;	/* end pointer */
	unsigned char*	trans = f->trans;
	int		sigdig = 1024;
	int		neg = f->rflag;	/* 0 for +, 1 for - */
	int		n;
	int		c;

	cp[1] = 0;

	/*
	 * sign
	 */

	if (trans[*(ep - 1)] & 0x10)
		neg ^= 1;
	while (xp < ep)
	{
		c = trans[*xp++];
		n = c & 0xF;
		putdig(n);
		sigdig++;
	}
	if (sigdig >= 2047)
	{
		sigdig = 2047;
		if (kp->keydisc->errorf)
			(*kp->keydisc->errorf)(kp, kp->keydisc, 1, "%-.*s: numeric field overflow", dp);
		dig = cp + 1;
		*dig = 0;
		nib = 0;
	}
	if (neg) sigdig = 2048 - sigdig;
	else sigdig = 2048 + sigdig;
	cp[0] = sigdig >> 4;
	cp[1] |= sigdig << 4;
	putdig(-1);
	return dig - cp + 1 - nib;
}

/*
 * random shuffle
 */

static int
#if __STD_C
key_j_code(Rskey_t* kp, Field_t* f, unsigned char* dp, unsigned char* cp, int len)
#else
key_j_code(kp, f, dp, cp, len)
Rskey_t*	kp;
Field_t*	f;
unsigned char*	dp;
unsigned char*	cp;
int		len;
#endif
{
	unsigned char*	xp = cp;
	int		c;

	while (--len >= 0)
	{
		c = *dp++;
		kp->shuffle = HASHPART(kp->shuffle, c);
		*xp++ = (kp->shuffle >> 4) & 0xff;
	}
	return xp - cp;
}

/*
 * Encode text field subject to options -r -fdi -b.
 * Fields are separated by 0 (or 255 if rflag is set)
 * the anti-ambiguity stuff prevents such codes from
 * happening otherwise by coding real zeros and ones
 * as 0x0101 and 0x0102, and similarly for complements
 */

static int
#if __STD_C
key_t_code(Rskey_t* kp, Field_t* f, unsigned char* dp, unsigned char* cp, int len)
#else
key_t_code(kp, f, dp, cp, len)
Rskey_t*	kp;
Field_t*	f;
unsigned char*	dp;
unsigned char*	cp;
int		len;
#endif
{
	unsigned char*	xp = cp;
	int		c;
	unsigned char*	keep = f->keep;
	unsigned char*	trans = f->trans;
	int		reverse = f->rflag ? ~0: 0;

	while (--len >= 0)
	{
		c = *dp++;
		if (keep[c])
		{
			c = trans[c];
			if(c <= 1)
			{
				/*
				 * anti-ambiguity
				 */

				*xp++ = 1 ^ reverse;
				c++;
			}
			else if (c >= 254)
			{
				*xp++ = 255 ^ reverse;
				c--;
			}
			*xp++ = c ^ reverse;
		}
	}
	if (xp)
		*xp++ = reverse;
	return xp - cp;
}

/*
 * sort by month name
 */

static const char*	month[] =
{
	"jan", "feb", "mar", "apr", "may", "jun",
	"jul", "aug", "sep", "oct", "nov", "dec"
};

static int
#if __STD_C
key_m_code(Rskey_t* kp, Field_t* f, unsigned char* dp, unsigned char* cp, int len)
#else
key_m_code(kp, f, dp, cp, len)
Rskey_t*	kp;
Field_t*	f;
unsigned char*	dp;
unsigned char*	cp;
int		len;
#endif
{
	register int	c;
	int		j = -1;
	int		i;
	unsigned char*	xp;
	unsigned char*	trans = f->trans;

	for (; len > 0 && blank(trans[*dp]); dp++, len--);
	if (len >= 3)
		while (++j < elementsof(month))
		{
			xp = (unsigned char*)month[j];
			for (i = 0; i < 3; i++)
			{
				c = trans[dp[i]];
				if (isupper(c))
					c = tolower(c);
				if (c != *xp++)
					break;
			}
			if (i >= 3)
				break;
		}
	*cp = j >= elementsof(month) ? 0 : j + 1;
	if (f->rflag)
		*cp ^= ~0;
	return 1;
}

/*
 * the recsort defkeyf
 * return encoded key for dat,datlen in key,keylen
 */

static int
#if __STD_C
code(Rs_t* rs, unsigned char* dat, int datlen, unsigned char* key, int keylen, Rsdisc_t* disc)
#else
code(rs, dat, datlen, key, keylen, disc)
Rs_t*		rs;
unsigned char*	dat;
int		datlen;
unsigned char*	key;
int		keylen;
Rsdisc_t*	disc;
#endif
{
	Rskey_t*	kp = (Rskey_t*)((char*)rs->disc - sizeof(Rskey_t));
	unsigned char*	cp;
	Field_t*	fp;
	unsigned char*	ep;
	unsigned char*	op = key;
	unsigned char*	xp = dat + datlen;
	int		t;
	int		np;
	int		m = kp->field.maxfield;
	unsigned char**	pp = kp->field.positions;

	pp[0] = dat;
	np = 1;
	switch (t = kp->tab)
	{
	case 0:
		for (cp = dat; cp < xp && np < m;)
		{
			while (cp < xp && blank(*cp))
				cp++;
			while (cp < xp && !blank(*cp))
				cp++;
			pp[np++] = cp;
		}
		break;
	case '\n':
		break;
	default:
		for (cp = dat; cp < xp && np < m;)
			if (*cp++ == t)
				pp[np++] = cp;
		break;
	}
	for (fp = kp->field.head; fp; fp = fp->next)
	{
		t = fp->begin.field;
		if (t < np)
		{
			cp = pp[t];
			if (fp->bflag && kp->field.global.next)
				while (cp < xp && blank(*cp))
					cp++;
			cp += fp->begin.index;
			if (cp > xp)
				cp = xp;
		}
		else cp = xp;
		t = fp->end.field;
		if (t < np)
		{
			if (fp->end.index < 0)
			{
				if (t >= np - 1)
					ep = xp;
				else
				{
					ep = pp[t + 1];
					if (kp->tab) ep--;
				}
			}
			else
			{
				ep = pp[t];
				if (fp->eflag)
					while(ep < xp && blank(*ep))
						ep++;
				ep += fp->end.index;
			}
			if (ep > xp)
				ep = xp;
			else if (ep < cp)
				ep = cp;
		}
		else ep = xp;
		op += (*fp->coder)(kp, fp, cp, op, ep - cp);
	}
	return op - key;
}

/*
 * conflict message
 */

static void
#if __STD_C
conflict(Rskey_t* kp, int c)
#else
conflict(kp, c)
Rskey_t*	kp;
int		c;
#endif
{
	(*kp->keydisc->errorf)(kp, kp->keydisc, 1, "%c: key type conflicts with previous value", c);
}

/*
 * nice band
 */

static int
#if __STD_C
checkfield(Rskey_t* kp, Field_t* fp, const char* key, int c)
#else
checkfield(kp, fp, key, c)
Rskey_t*	kp;
Field_t*	fp;
char*		key;
int		c;
#endif
{
	if (c || fp->begin.field < 0 || fp->end.field < 0 || fp->begin.index < 0 || fp->end.index < -1)
	{
		if (kp->keydisc->errorf)
		{
			if (key)
				(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "%s: invalid key field specification", key);
			else
				(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "field[%d]: invalid key field specification", fp->index);
		}
		kp->keydisc->flags |= RSKEY_ERROR;
		return -1;
	}
	if (kp->keydisc->errorf && fp->coder == key_n_code && fp->keep)
		(*kp->keydisc->errorf)(kp, kp->keydisc, 1, "n: key type conflicts with d,i");
	return 0;
}

/*
 * add coding function
 */

static void
#if __STD_C
addcoder(Rskey_t* kp, Field_t* fp, Coder_t np, int c, int b)
#else
addcoder(kp, fp, np, c, b)
Rskey_t*	kp;
Field_t*	fp;
Coder_t		np;
int		c;
int		b;
#endif
{
	NoP(kp);
	if (kp->keydisc->errorf && fp->coder && fp->coder != np)
		conflict(kp, c);
	fp->coder = np;
	fp->flag = c;
	fp->binary = b;
}

/*
 * add translation table
 */

static void
#if __STD_C
addtable(Rskey_t* kp, int c, unsigned char** op, unsigned char* np)
#else
addtable(kp, c, op, np)
Rskey_t*	kp;
int		c;
unsigned char**	op;
unsigned char*	np;
#endif
{
	NoP(kp);
	if (kp->keydisc->errorf && *op && *op != np)
		conflict(kp, c);
	*op = np;
}

/*
 * add a sort key field option c
 */

static int
#if __STD_C
addopt(Rskey_t* kp, register Field_t* fp, register char* s, int end)
#else
addopt(kp, fp, s, end)
Rskey_t*		kp;
register Field_t*	fp;
register char*		s;
int			end;
#endif
{
	char*		b = s;
	char*		e;
	int		c;
	int		x;

	switch (c = *s++)
	{
	case 0:
		return 0;
	case 'a':
		if (!fp->aflag)
		{
			fp->aflag = 1;
			if (!kp->field.prev)
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "field[%d]: global accumulate invalid", fp->index);
				kp->keydisc->flags |= RSKEY_ERROR;
				return 0;
			}
			(kp->field.tail = kp->field.prev)->next = 0;
			kp->field.prev = 0;
			if (kp->accumulate.tail)
				kp->accumulate.tail->next = fp;
			else
				kp->accumulate.head = kp->accumulate.tail = fp;
		}
		return s - b;
	case 'b':
		if (end)
			fp->eflag = 1;
		else
			fp->bflag = 1;
		return s - b;
	case 'C':
		switch (*s++)
		{
		case 'a':
			x = CC_ASCII;
			break;
		case 'e':
			x = CC_EBCDIC_E;
			break;
		case 'i':
			x = CC_EBCDIC_I;
			break;
		case 'o':
			x = CC_EBCDIC_O;
			break;
		case 'x':
			x = CC_NATIVE;
			break;
		default:
			if (kp->keydisc->errorf)
				(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "field[%d]: %s: invalid code set", fp->index, s - 1);
			kp->keydisc->flags |= RSKEY_ERROR;
			return 0;
		}
		if (*s == ':')
			s++;
		switch (*s++)
		{
		case 'a':
			x = CCOP(x, CC_ASCII);
			break;
		case 'e':
			x = CCOP(x, CC_EBCDIC_E);
			break;
		case 'i':
			x = CCOP(x, CC_EBCDIC_I);
			break;
		case 'o':
			x = CCOP(x, CC_EBCDIC_O);
			break;
		case 'x':
			x = CCOP(x, CC_NATIVE);
			break;
		default:
			s--;
			break;
		}
		if (x != CC_NATIVE && CCIN(x) != CCOUT(x))
		{
			fp->code = x;
			if (fp == kp->field.head)
				kp->code = fp->code;
		}
		return s - b;
	case 'd':
		addtable(kp, c, &fp->keep, kp->state->dict);
		break;
	case 'f':
		addtable(kp, c, &fp->trans, kp->state->fold);
		break;
	case 'g':
	case 'n':
		addcoder(kp, fp, key_n_code, c, 0);
		break;
	case 'i':
		addtable(kp, c, &fp->keep, kp->state->print);
		break;
	case 'J':
		kp->shuffle = strtoul(s, &e, 0);
		s = e;
		if (!kp->shuffle)
			kp->shuffle = (unsigned long)time(NiL) * (unsigned long)getpid();
		addcoder(kp, fp, key_j_code, c, 0);
		break;
	case 'M':
		addcoder(kp, fp, key_m_code, c, 0);
		break;
	case 'p':
		addcoder(kp, fp, key_p_code, c, 1);
		break;
	case 'r':
		fp->rflag = 1;
		return s - b;
	case 'Z':
		addcoder(kp, fp, key_z_code, c, 1);
		break;
	default:
		return 0;
	}
	kp->coded = 1;
	if (kp->keydisc->errorf && fp != kp->field.tail)
		(*kp->keydisc->errorf)(kp, kp->keydisc, 1, "field spec precedes global option %c", c);
	return s - b;
}

/*
 * add sort key options in s
 * all!=0 applies to all fields,
 * otherwise the current field
 */

int
#if __STD_C
rskeyopt(Rskey_t* kp, const char* key, int all)
#else
rskeyopt(kp, key, all)
Rskey_t*	kp;
char*		key;
int		all;
#endif
{
	register Field_t*	fp;
	register int		i;
	char*			s;

	fp = all ? kp->field.head : kp->field.tail;
	s = (char*)key;
	while (i = addopt(kp, fp, s, 0))
		s += i;
	if (fp->standard && (*s == ',' || *s == ' '))
	{
		s++;
		if ((fp->end.field = (int)strtol(s, (char**)&s, 10) - 1) > kp->field.maxfield)
			kp->field.maxfield = fp->end.field;
		if (*s == '.' && !(fp->end.index = (int)strtol(s + 1, &s, 10)))
			fp->end.index = -1;
		while (i = addopt(kp, fp, s, 1))
			s += i;
	}
	return checkfield(kp, fp, key, *s);
}

/*
 * add a sort key
 */

int
#if __STD_C
rskey(Rskey_t* kp, const char* key, int obsolete)
#else
rskey(kp, key, obsolete)
Rskey_t*	kp;
char*		key;
int		obsolete;
#endif
{
	register Field_t*	fp;
	int			o;
	int			n;
	int			standard;
	char*			s;
	char*			t;
	char			buf[32];

	s = (char*)key;
	if (*s == '.')
	{
		n = (int)strtol(s + 1, &t, 10);
		if (!*t)
		{
			if (n != kp->fixed && kp->fixed)
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "%s: fixed record length mismatch -- %d expected", key, kp->fixed);
				kp->keydisc->flags |= RSKEY_ERROR;
				return -1;
			}
			kp->fixed = n;
			kp->disc->data = REC_F_TYPE(n);
			return 0;
		}
	}
	n = (int)strtol(s, &t, 10);
	if (s == t)
		n = *s != ':';
	else
		s = t;
	if ((standard = !obsolete) && *s == ':')
	{
		if (n)
		{
			if (n != kp->fixed && kp->fixed)
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "%s: fixed record key length mismatch -- %d expected", key, kp->fixed);
				kp->keydisc->flags |= RSKEY_ERROR;
				return -1;
			}
			kp->fixed = n;
			kp->disc->data = REC_F_TYPE(n);
		}
		if (!*++s)
			return 0;
		n = (int)strtol(s, &s, 10);
		o = *s == ':' ? (int)(strtol(s + 1, &s, 10) + 1) : 1;
		if (kp->fixed && (o + n) > kp->fixed)
		{
			if (kp->keydisc->errorf)
				(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "%s: fixed field exceeds record length %d", key, kp->fixed);
			kp->keydisc->flags |= RSKEY_ERROR;
			return -1;
		}
		key = (const char*)(s = buf);
		sfsprintf(s, sizeof(buf), ".%d,1.%d", o, o + n - 1);
		n = 1;
	}
	if (obsolete == '-')
	{
		if (!kp->field.global.next && rskey(kp, "0", 1))
			return -1;
		s = (char*)key;
		if ((kp->field.tail->end.field = *s == '.' ? kp->field.tail->begin.field : (int)strtol(s, &s, 10)) > kp->field.maxfield)
			kp->field.maxfield = kp->field.tail->end.field;
		if (*s == '.')
			kp->field.tail->end.index = (int)strtol(s + 1, &s, 10);
		else
			kp->field.tail->end.field--;
		if (!kp->field.tail->end.index)
			kp->field.tail->end.index = -1;
	}
	else if (!(fp = vmnewof(Vmheap, 0, Field_t, 1, 0)))
	{
		if (kp->keydisc->errorf)
			(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "out of space [field]");
		kp->keydisc->flags |= RSKEY_ERROR;
		return -1;
	}
	else
	{
		fp->index = ++kp->field.index;
		kp->field.prev = kp->field.tail;
		kp->field.tail = kp->field.tail->next = fp;
		fp->bflag = fp->eflag = 0;
		fp->standard = standard;
		if ((fp->begin.field = n - fp->standard) > kp->field.maxfield)
			kp->field.maxfield = fp->begin.field;
		fp->end.field = MAXFIELD;
		fp->code = kp->field.head->code;
		if (*s == '.')
		{
			fp->begin.index = (int)strtol(s + 1, &s, 10) - fp->standard;
			if (*s == '.')
			{
				fp->end.field = fp->begin.field;
				fp->end.index = fp->begin.index + (int)strtol(s + 1, &s, 10);
			}
		}
	}
	return *s ? rskeyopt(kp, s, 0) : 0;
}

/*
 * set up field character transform
 */

static int
#if __STD_C
transform(Rskey_t* kp, register Field_t* fp)
#else
transform(kp, fp)
Rskey_t*		kp;
register Field_t*	fp;
#endif
{
	register unsigned char*	m;
	register unsigned char*	t;
	register unsigned char*	x;
	register int		c;

	if (fp->code)
	{
		if (fp->binary)
		{
			if (CCCONVERT(fp->code))
				fp->trans = ccmap(fp->code, 0);
		}
		else if (m = ccmap(fp->code, CC_NATIVE))
		{
			if (!fp->trans)
				fp->trans = m;
			else if (!(x = vmnewof(Vmheap, 0, unsigned char, UCHAR_MAX, 1)))
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "out of space");
				kp->keydisc->flags |= RSKEY_ERROR;
				return -1;
			}
			else
			{
				t = fp->trans;
				for (c = 0; c <= UCHAR_MAX; c++)
					x[c] = t[m[c]];
				fp->trans = x;
				fp->freetrans = 1;
			}
		}
	}
	if (!fp->trans)
		fp->trans = kp->state->ident;
	return 0;
}

/*
 * initialize key info after all rskey() calls
 */

int
#if __STD_C
rskeyinit(register Rskey_t* kp)
#else
rskeyinit(kp)
register Rskey_t*	kp;
#endif
{
	register long		n;
	register Field_t*	fp;
	long			m;
	size_t			z;

	static char*		in[] = { "-", 0 };

	/*
	 * finalize the fields
	 */

	if (checkfield(kp, kp->field.tail, NiL, 0))
		return -1;
	fp = kp->field.head;
	if (!fp->coder)
	{
		fp->coder = key_t_code;
		fp->flag = 't';
	}
	if (transform(kp, fp))
		return -1;
	if (!fp->keep)
		fp->keep = kp->state->all;
	if (fp->rflag)
	{
		fp->rflag = 0;
		kp->type |= RS_REVERSE;
	}
	kp->code = fp->code;
	while (fp = fp->next)
	{
		n = 0;
		if (!fp->coder)
		{
			fp->coder = key_t_code;
			fp->flag = 't';
		}
		else n = 1;
		if(!fp->keep) fp->keep = kp->state->all;
		else n = 1;
		if (!n && !fp->trans && !fp->bflag && !fp->eflag && !fp->rflag)
		{
			fp->coder = kp->field.global.coder;
			fp->code = kp->field.global.code;
			fp->flag = kp->field.global.flag;
			fp->trans = kp->field.global.trans;
			fp->keep = kp->field.global.keep;
			fp->rflag = kp->field.global.rflag;
			fp->bflag = kp->field.global.bflag;
			if (fp->standard)
				fp->eflag = kp->field.global.bflag;
		}
		else
		{
			if (transform(kp, fp))
				return -1;
			if (kp->type & RS_REVERSE)
				fp->rflag = !fp->rflag;
		}
		if (fp->standard)
		{
			if (!fp->end.index)
				fp->end.index--;
		}
		else if (!fp->end.index && fp->end.field)
		{
			if (kp->tab && fp->eflag)
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "skipping blanks right after tab-char is ill-defined");
				kp->keydisc->flags |= RSKEY_ERROR;
				return -1;
			}
			fp->end.index--;
		}
		if (kp->fixed)
		{
			if (fp->begin.index > kp->fixed)
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "field[%d]: begin index %d is greater than fixed record size", fp->index, fp->begin.index);
				kp->keydisc->flags |= RSKEY_ERROR;
				return -1;
			}
			if (fp->end.index > kp->fixed)
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "field[%d]: end index %d is greater than fixed record size", fp->index, fp->end.index);
				kp->keydisc->flags |= RSKEY_ERROR;
				return -1;
			}
		}
	}
	fp = kp->field.head;
	if (fp = fp->next)
	{
		kp->field.head = fp;
		if (!fp->next && !kp->tab && !fp->begin.field && !fp->end.field && fp->end.index > 0 && fp->flag == 't' && fp->trans == kp->state->ident && fp->keep == kp->state->all && !fp->bflag && !fp->eflag && !fp->rflag)
		{
			kp->disc->type |= RS_KSAMELEN;
			kp->disc->key = fp->begin.index;
			kp->disc->keylen = fp->end.index - fp->begin.index;
		}
		else
			kp->coded = 1;
	}
	if (kp->coded)
	{
		kp->field.maxfield += 2;
		kp->disc->defkeyf = code;
		kp->disc->key = 2 * kp->field.maxfield;
		if (!(kp->field.positions = vmnewof(Vmheap, 0, unsigned char*, kp->field.maxfield, 0)))
		{
			if (kp->keydisc->errorf)
				(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "out of space [positions]");
			kp->keydisc->flags |= RSKEY_ERROR;
			return -1;
		}
	}
	if (kp->fixed)
	{
		kp->disc->type |= RS_DSAMELEN;
		kp->disc->data = kp->fixed;
		if (kp->disc->keylen < 0)
			kp->disc->keylen = 0;
	}

	/*
	 * limit the sizes
	 */

	z = datasize() / 3;
	if (kp->nproc > 1)
		z /= 2;
	if (kp->insize > z)
		kp->insize = z;
	if (kp->outsize > z)
		kp->outsize = z;

	/*
	 * reconcile the sizes
	 */

	if (!(n = kp->alignsize))
		n = SF_BUFSIZE;
	if (n & (n - 1))
	{
		for (m = 1; m < n; m <<= 1)
			if (m >= (LONG_MAX >> CHAR_BIT))
			{
				if (kp->keydisc->errorf)
					(*kp->keydisc->errorf)(kp, kp->keydisc, 2, "%ld: invalid alignment size", n);
				kp->keydisc->flags |= RSKEY_ERROR;
				return -1;
			}
		if (kp->keydisc->errorf)
			(*kp->keydisc->errorf)(kp, kp->keydisc, 1, "%ld: alignment size rounded to %ld", n, m);
		n = m;
	}
	kp->alignsize = n--;
	kp->insize = (kp->insize < kp->alignsize) ? kp->alignsize : roundof(kp->insize, kp->alignsize);
	kp->outsize = (kp->outsize && kp->outsize < kp->alignsize) ? kp->alignsize : roundof(kp->outsize, kp->alignsize);
	kp->procsize = (kp->procsize < kp->alignsize) ? kp->alignsize : roundof(kp->procsize, kp->alignsize);
	if (kp->procsize > kp->insize)
		kp->procsize = kp->insize;
	if (kp->insize == kp->alignsize && kp->alignsize > 1)
		kp->alignsize /= 2;

	/*
	 * no input files equivalent to "-"
	 */

	if (!kp->input || !*kp->input)
		kp->input = in;
	return (kp->keydisc->flags & RSKEY_ERROR) ? -1 : 0;
}
