# ifndef FUNCDEF
# define INCLUDE_CTYPE
# include "kfun.h"
# include "parse.h"
# include "asn.h"
# endif


# ifdef FUNCDEF
FUNCDEF("encrypt", kf_encrypt, pt_encrypt)
# else
char pt_encrypt[] = { C_TYPECHECKED | C_STATIC, 2, 1, 0, 9, T_MIXED, T_STRING,
		      T_STRING, T_STRING };

/*
 * NAME:	kfun->encrypt()
 * DESCRIPTION:	encrypt a string
 */
int kf_encrypt(f, nargs)
register frame *f;
register int nargs;
{
    extern string *P_encrypt_des_key P((frame*, string*));
    extern string *P_encrypt_des P((frame*, string*, string*));
    string *str;

    str = (string *) NULL;
    if (nargs == 2) {
	if (f->sp[1].u.string->len == 7 &&
	    strcmp(f->sp[1].u.string->text, "DES key") == 0) {
	    /*
	     * prepare key for encryption
	     */
	    str = P_encrypt_des_key(f, f->sp->u.string);
	}
    } else {
	if (f->sp[2].u.string->len == 3 &&
	    strcmp(f->sp[2].u.string->text, "DES") == 0) {
	    /*
	     * encrypt
	     */
	    str = P_encrypt_des(f, f->sp[1].u.string, f->sp->u.string);
	}
    }
    if (str == (string *) NULL) {
	error("Unknown cipher");
    }

    while (--nargs != 0) {
	str_del((f->sp++)->u.string);
    }
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("decrypt", kf_decrypt, pt_decrypt)
# else
char pt_decrypt[] = { C_TYPECHECKED | C_STATIC, 2, 1, 0, 9, T_MIXED, T_STRING,
		      T_STRING, T_STRING };

/*
 * NAME:	kfun->decrypt()
 * DESCRIPTION:	decrypt a string
 */
int kf_decrypt(f, nargs)
register frame *f;
register int nargs;
{
    extern string *P_decrypt_des_key P((frame*, string*));
    extern string *P_encrypt_des P((frame*, string*, string*));
    string *str;

    str = (string *) NULL;
    if (nargs == 2) {
	if (f->sp[1].u.string->len == 7 &&
	    strcmp(f->sp[1].u.string->text, "DES key") == 0) {
	    /*
	     * prepare key for decryption
	     */
	    str = P_decrypt_des_key(f, f->sp->u.string);
	}
    } else {
	if (f->sp[2].u.string->len == 3 &&
	    strcmp(f->sp[2].u.string->text, "DES") == 0) {
	    /*
	     * decrypt
	     */
	    str = P_encrypt_des(f, f->sp[1].u.string, f->sp->u.string);
	}
    }
    if (str == (string *) NULL) {
	error("Unknown cipher");
    }

    while (--nargs != 0) {
	str_del((f->sp++)->u.string);
    }
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("ctime", kf_ctime, pt_ctime)
# else
char pt_ctime[] = { C_TYPECHECKED | C_STATIC, 1, 0, 0, 7, T_STRING, T_INT };

/*
 * NAME:	kfun->ctime()
 * DESCRIPTION:	convert a time value to a string
 */
int kf_ctime(f)
frame *f;
{
    char buf[26];

    i_add_ticks(f, 5);
    P_ctime(buf, f->sp->u.number);
    PUT_STRVAL(f->sp, str_new(buf, 24L));

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("explode", kf_explode, pt_explode)
# else
char pt_explode[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8,
		      T_STRING | (1 << REFSHIFT), T_STRING, T_STRING };

/*
 * NAME:	kfun->explode()
 * DESCRIPTION:	explode a string
 */
int kf_explode(f)
register frame *f;
{
    register unsigned int len, slen, size;
    register char *p, *s;
    register value *v;
    array *a;

    p = f->sp[1].u.string->text;
    len = f->sp[1].u.string->len;
    s = f->sp->u.string->text;
    slen = f->sp->u.string->len;

    if (len == 0) {
	/*
	 * exploding "" always gives an empty array
	 */
	a = arr_new(f->data, 0L);
    } else if (slen == 0) {
	/*
	 * the sepatator is ""; split string into single characters
	 */
	a = arr_new(f->data, (long) len);
	for (v = a->elts; len > 0; v++, --len) {
	    PUT_STRVAL(v, str_new(p, 1L));
	    p++;
	}
    } else {
	/*
	 * split up the string with the separator
	 */
	size = 1;
	if (len >= slen && memcmp(p, s, slen) == 0) {
	    /* skip leading separator */
	    p += slen;
	    len -= slen;
	}
	while (len > slen) {
	    if (memcmp(p, s, slen) == 0) {
		/* separator found */
		p += slen;
		len -= slen;
		size++;
	    } else {
		/* next char */
		p++;
		--len;
	    }
	}

	a = arr_new(f->data, (long) size);
	v = a->elts;

	p = f->sp[1].u.string->text;
	len = f->sp[1].u.string->len;
	size = 0;
	if (len > slen && memcmp(p, s, slen) == 0) {
	    /* skip leading separator */
	    p += slen;
	    len -= slen;
	}
	while (len > slen) {
	    if (memcmp(p, s, slen) == 0) {
		/* separator found */
		PUT_STRVAL(v, str_new(p - size, (long) size));
		v++;
		p += slen;
		len -= slen;
		size = 0;
	    } else {
		/* next char */
		p++;
		--len;
		size++;
	    }
	}
	if (len != slen || memcmp(p, s, slen) != 0) {
	    /* remainder isn't a sepatator */
	    size += len;
	    p += len;
	}
	/* final array element */
	PUT_STRVAL(v, str_new(p - size, (long) size));
    }

    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_ARRVAL(f->sp, a);
    i_add_ticks(f, (Int) 2 * a->size);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("implode", kf_implode, pt_implode)
# else
char pt_implode[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8, T_STRING,
		      T_STRING | (1 << REFSHIFT), T_STRING };

/*
 * NAME:	kfun->implode()
 * DESCRIPTION:	implode an array
 */
int kf_implode(f)
register frame *f;
{
    register long len;
    register unsigned int i, slen;
    register char *p, *s;
    register value *v;
    string *str;

    s = f->sp->u.string->text;
    slen = f->sp->u.string->len;

    /* first, determine the size of the imploded string */
    i = f->sp[1].u.array->size;
    i_add_ticks(f, i);
    if (i != 0) {
	len = (i - 1) * (long) slen;	/* size of all separators */
	for (v = d_get_elts(f->sp[1].u.array); i > 0; v++, --i) {
	    if (v->type != T_STRING) {
		/* not a (string *) */
		return 1;
	    }
	    len += v->u.string->len;
	}
	str = str_new((char *) NULL, len);

	/* create the imploded string */
	p = str->text;
	for (i = f->sp[1].u.array->size, v -= i; i > 1; --i, v++) {
	    /* copy array part */
	    memcpy(p, v->u.string->text, v->u.string->len);
	    p += v->u.string->len;
	    /* copy separator */
	    memcpy(p, s, slen);
	    p += slen;
	}
	/* copy final array part */
	memcpy(p, v->u.string->text, v->u.string->len);
    } else {
	/* zero size array gives zero size string */
	str = str_new((char *) NULL, 0L);
    }

    str_del((f->sp++)->u.string);
    arr_del(f->sp->u.array);
    PUT_STRVAL(f->sp, str);
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("random", kf_random, pt_random)
# else
char pt_random[] = { C_TYPECHECKED | C_STATIC, 1, 0, 0, 7, T_INT, T_INT };

/*
 * NAME:	kfun->random()
 * DESCRIPTION:	return a random number
 */
int kf_random(f)
register frame *f;
{
    i_add_ticks(f, 1);
    PUT_INT(f->sp, (f->sp->u.number > 0) ? P_random() % f->sp->u.number : 0);
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("sscanf", kf_sscanf, pt_sscanf)
# else
char pt_sscanf[] = { C_TYPECHECKED | C_STATIC | C_ELLIPSIS, 2, 1, 0, 9, T_INT,
		     T_STRING, T_STRING, T_LVALUE };

/*
 * NAME:	match
 * DESCRIPTION:	match a string possibly including %%, up to the next %[sdfc] or
 *		the end of the string
 */
static bool match(f, s, flenp, slenp)
register char *f, *s;
unsigned int *flenp, *slenp;
{
    register char *p;
    register unsigned int flen, slen;

    flen = *flenp;
    slen = *slenp;

    while (flen > 0) {
	/* look for first % */
	p = (char *) memchr(f, '%', flen);

	if (p == (char *) NULL) {
	    /* no remaining % */
	    if (memcmp(f, s, flen) == 0) {
		*slenp -= slen - flen;
		return TRUE;
	    } else {
		return FALSE;	/* no match */
	    }
	}

	if (p[1] == '%') {
	    /* %% */
	    if (memcmp(f, s, ++p - f) == 0) {
		/* matched up to and including the first % */
		s += p - f;
		slen -= p - f;
		flen -= ++p - f;
		f = p;
	    } else {
		return FALSE;	/* no match */
	    }
	} else if (memcmp(f, s, p - f) == 0) {
	    /* matched up to the first % */
	    *flenp -= flen - (p - f);
	    *slenp -= slen - (p - f);
	    return TRUE;
	} else {
	    return FALSE;	/* no match */
	}
    }

    *slenp -= slen;
    return TRUE;
}

/*
 * NAME:	kfun->sscanf()
 * DESCRIPTION:	scan a string
 */
int kf_sscanf(f, nargs)
register frame *f;
int nargs;
{
    register unsigned int flen, slen, size;
    register char *format, *x;
    unsigned int fl, sl;
    int matches;
    char *s;
    Int i;
    xfloat flt;
    bool skip;

    if (nargs > MAX_LOCALS + 2) {
	return 4;
    }
    s = f->sp[nargs - 1].u.string->text;
    slen = f->sp[nargs - 1].u.string->len;
    nargs -= 2;
    format = f->sp[nargs].u.string->text;
    flen = f->sp[nargs].u.string->len;
    i_reverse(f, nargs);

    i_add_ticks(f, 8 * nargs);
    matches = 0;

    while (flen > 0) {
	if (format[0] != '%' || format[1] == '%') {
	    /* match initial part */
	    fl = flen;
	    sl = slen;
	    if (!match(format, s, &fl, &sl) || fl == flen) {
		goto no_match;
	    }
	    format += fl;
	    flen -= fl;
	    s += sl;
	    slen -= sl;
	}

	/* skip first % */
	format++;
	--flen;

	/*
	 * check for %*
	 */
	if (*format == '*') {
	    /* no assignment */
	    format++;
	    --flen;
	    skip = TRUE;
	} else {
	    skip = FALSE;
	}

	--flen;
	switch (*format++) {
	case 's':
	    /* %s */
	    if (format[0] == '%' && format[1] != '%') {
		switch ((format[1] == '*') ? format[2] : format[1]) {
		case 'd':
		    /*
		     * %s%d
		     */
		    size = slen;
		    x = s;
		    while (!isdigit(*x)) {
			if (slen == 0) {
			    goto no_match;
			}
			if (x[0] == '-' && isdigit(x[1])) {
			    break;
			}
			x++;
			--slen;
		    }
		    size -= slen;
		    break;

		case 'f':
		    /*
		     * %s%f
		     */
		    size = slen;
		    x = s;
		    while (!isdigit(*x)) {
			if (slen == 0) {
			    goto no_match;
			}
			if ((x[0] == '-' || x[0] == '.') && isdigit(x[1])) {
			    break;
			}
			x++;
			--slen;
		    }
		    size -= slen;
		    break;

		default:
		    error("Bad sscanf format string");
		}
	    } else {
		/*
		 * %s followed by non-%
		 */
		if (flen == 0) {
		    /* match whole string */
		    size = slen;
		    x = s + slen;
		    slen = 0;
		} else {
		    /* get # of chars to match after string */
		    for (x = format, size = 0; (x - format) != flen;
			 x++, size++) {
			x = (char *) memchr(x, '%', flen - (x - format));
			if (x == (char *) NULL) {
			    x = format + flen;
			    break;
			} else if (x[1] != '%') {
			    break;
			}
		    }
		    size = (x - format) - size;

		    x = s;
		    for (;;) {
			sl = slen - (x - s);
			if (sl < size) {
			    goto no_match;
			}
			x = (char *) memchr(x, format[0], sl - size + 1);
			if (x == (char *) NULL) {
			    goto no_match;
			}
			fl = flen;
			if (match(format, x, &fl, &sl)) {
			    format += fl;
			    flen -= fl;
			    size = x - s;
			    x += sl;
			    slen -= size + sl;
			    break;
			}
			x++;
		    }
		}
	    }

	    if (!skip) {
		if (nargs == 0) {
		    error("No lvalue for %%s");
		}
		--nargs;
		PUSH_STRVAL(f, str_new(s, (long) size));
		i_store(f);
		f->sp->u.string->ref--;
		f->sp += 2;
	    }
	    s = x;
	    break;

	case 'd':
	    /* %d */
	    x = s;
	    while (slen != 0 && *x == ' ') {
		x++;
		--slen;
	    }
	    s = x;
	    i = strtoint(&s);
	    if (s == x) {
		goto no_match;
	    }
	    slen -= (s - x);

	    if (!skip) {
		if (nargs == 0) {
		    error("No lvalue for %%d");
		}
		--nargs;
		PUSH_INTVAL(f, i);
		i_store(f);
		f->sp += 2;
	    }
	    break;

	case 'f':
	    /* %f */
	    x = s;
	    while (slen != 0 && *x == ' ') {
		x++;
		--slen;
	    }
	    s = x;
	    if (!flt_atof(&s, &flt) || s == x) {
		goto no_match;
	    }
	    slen -= (s - x);

	    if (!skip) {
		if (nargs == 0) {
		    error("No lvalue for %%f");
		}
		--nargs;
		PUSH_FLTVAL(f, flt);
		i_store(f);
		f->sp += 2;
	    }
	    break;

	case 'c':
	    /* %c */
	    if (slen == 0) {
		goto no_match;
	    }
	    if (!skip) {
		if (nargs == 0) {
		    error("No lvalue for %%c");
		}
		--nargs;
		PUSH_INTVAL(f, UCHAR(*s));
		i_store(f);
		f->sp += 2;
	    }
	    s++;
	    --slen;
	    break;

	default:
	    error("Bad sscanf format string");
	}
	matches++;
    }

no_match:
    if (nargs > 0) {
	i_pop(f, nargs);	/* pop superfluous arguments */
    }
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_INTVAL(f->sp, matches);
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("parse_string", kf_parse_string, pt_parse_string)
# else
char pt_parse_string[] = { C_TYPECHECKED | C_STATIC, 2, 1, 0, 9,
			   T_MIXED | (1 << REFSHIFT), T_STRING, T_STRING,
			   T_INT };

/*
 * NAME:	kfun->parse_string()
 * DESCRIPTION:	parse a string
 */
int kf_parse_string(f, nargs)
register frame *f;
int nargs;
{
    Int maxalt;
    array *a;

    if (nargs > 2) {
	maxalt = (f->sp++)->u.number + 1;
	if (maxalt <= 0) {
	    return 3;
	}
    } else {
	maxalt = 1;	/* default: just one valid parse tree */
    }

    if (OBJR(f->oindex)->flags & O_SPECIAL) {
	error("parse_string() from special purpose object");
    }

    a = ps_parse_string(f, f->sp[1].u.string, f->sp->u.string, maxalt);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);

    if (a != (array *) NULL) {
	/* return parse tree */
	PUT_ARRVAL(f->sp, a);
    } else {
	/* parsing failed */
	*f->sp = nil_value;
    }
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("hash_crc16", kf_hash_crc16, pt_hash_crc16)
# else
char pt_hash_crc16[] = { C_TYPECHECKED | C_STATIC | C_ELLIPSIS, 1, 1, 0, 8,
			 T_INT, T_STRING, T_STRING };

/*
 * NAME:	kfun->hash_crc16()
 * DESCRIPTION:	Compute a 16 bit cyclic redundancy code for a string.
 *		Based on "A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS",
 *		by Ross N. Williams.
 *
 *		    Name:	"CRC-16/CCITT"	(supposedly)
 *		    Width:	16
 *		    Poly:	1021		(X^16 + X^12 + X^5 + 1)
 *		    Init:	FFFF
 *		    RefIn:	False
 *		    RefOut:	False
 *		    XorOut:	0000
 *		    Check:	29B1
 */
int kf_hash_crc16(f, nargs)
register frame *f;
int nargs;
{
    static unsigned short crctab[] = {
	0x0000, 0x2110, 0x4220, 0x6330, 0x8440, 0xa550, 0xc660, 0xe770,
	0x0881, 0x2991, 0x4aa1, 0x6bb1, 0x8cc1, 0xadd1, 0xcee1, 0xeff1,
	0x3112, 0x1002, 0x7332, 0x5222, 0xb552, 0x9442, 0xf772, 0xd662,
	0x3993, 0x1883, 0x7bb3, 0x5aa3, 0xbdd3, 0x9cc3, 0xfff3, 0xdee3,
	0x6224, 0x4334, 0x2004, 0x0114, 0xe664, 0xc774, 0xa444, 0x8554,
	0x6aa5, 0x4bb5, 0x2885, 0x0995, 0xeee5, 0xcff5, 0xacc5, 0x8dd5,
	0x5336, 0x7226, 0x1116, 0x3006, 0xd776, 0xf666, 0x9556, 0xb446,
	0x5bb7, 0x7aa7, 0x1997, 0x3887, 0xdff7, 0xfee7, 0x9dd7, 0xbcc7,
	0xc448, 0xe558, 0x8668, 0xa778, 0x4008, 0x6118, 0x0228, 0x2338,
	0xccc9, 0xedd9, 0x8ee9, 0xaff9, 0x4889, 0x6999, 0x0aa9, 0x2bb9,
	0xf55a, 0xd44a, 0xb77a, 0x966a, 0x711a, 0x500a, 0x333a, 0x122a,
	0xfddb, 0xdccb, 0xbffb, 0x9eeb, 0x799b, 0x588b, 0x3bbb, 0x1aab,
	0xa66c, 0x877c, 0xe44c, 0xc55c, 0x222c, 0x033c, 0x600c, 0x411c,
	0xaeed, 0x8ffd, 0xeccd, 0xcddd, 0x2aad, 0x0bbd, 0x688d, 0x499d,
	0x977e, 0xb66e, 0xd55e, 0xf44e, 0x133e, 0x322e, 0x511e, 0x700e,
	0x9fff, 0xbeef, 0xdddf, 0xfccf, 0x1bbf, 0x3aaf, 0x599f, 0x788f,
	0x8891, 0xa981, 0xcab1, 0xeba1, 0x0cd1, 0x2dc1, 0x4ef1, 0x6fe1,
	0x8010, 0xa100, 0xc230, 0xe320, 0x0450, 0x2540, 0x4670, 0x6760,
	0xb983, 0x9893, 0xfba3, 0xdab3, 0x3dc3, 0x1cd3, 0x7fe3, 0x5ef3,
	0xb102, 0x9012, 0xf322, 0xd232, 0x3542, 0x1452, 0x7762, 0x5672,
	0xeab5, 0xcba5, 0xa895, 0x8985, 0x6ef5, 0x4fe5, 0x2cd5, 0x0dc5,
	0xe234, 0xc324, 0xa014, 0x8104, 0x6674, 0x4764, 0x2454, 0x0544,
	0xdba7, 0xfab7, 0x9987, 0xb897, 0x5fe7, 0x7ef7, 0x1dc7, 0x3cd7,
	0xd326, 0xf236, 0x9106, 0xb016, 0x5766, 0x7676, 0x1546, 0x3456,
	0x4cd9, 0x6dc9, 0x0ef9, 0x2fe9, 0xc899, 0xe989, 0x8ab9, 0xaba9,
	0x4458, 0x6548, 0x0678, 0x2768, 0xc018, 0xe108, 0x8238, 0xa328,
	0x7dcb, 0x5cdb, 0x3feb, 0x1efb, 0xf98b, 0xd89b, 0xbbab, 0x9abb,
	0x754a, 0x545a, 0x376a, 0x167a, 0xf10a, 0xd01a, 0xb32a, 0x923a,
	0x2efd, 0x0fed, 0x6cdd, 0x4dcd, 0xaabd, 0x8bad, 0xe89d, 0xc98d,
	0x267c, 0x076c, 0x645c, 0x454c, 0xa23c, 0x832c, 0xe01c, 0xc10c,
	0x1fef, 0x3eff, 0x5dcf, 0x7cdf, 0x9baf, 0xbabf, 0xd98f, 0xf89f,
	0x176e, 0x367e, 0x554e, 0x745e, 0x932e, 0xb23e, 0xd10e, 0xf01e
    };
    register unsigned short crc;
    register int i;
    register ssizet len;
    register char *p;
    register Int cost;

    cost = 0;
    for (i = nargs; --i >= 0; ) {
	cost += f->sp[i].u.string->len;
    }
    cost = 3 * nargs + (cost >> 2);
    if (!f->rlim->noticks && f->rlim->ticks <= cost) {
	f->rlim->ticks = 0;
	error("Out of ticks");
    }
    i_add_ticks(f, cost);

    crc = 0xffff;
    for (i = nargs; --i >= 0; ) {
	p = f->sp[i].u.string->text;
	for (len = f->sp[i].u.string->len; len != 0; --len) {
	    crc = (crc >> 8) ^ crctab[UCHAR(crc ^ *p++)];
	}
	str_del(f->sp[i].u.string);
    }
    crc = (crc >> 8) + (crc << 8);

    f->sp += nargs - 1;
    PUT_INTVAL(f->sp, crc);
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("hash_crc32", kf_hash_crc32, pt_hash_crc32)
# else
char pt_hash_crc32[] = { C_TYPECHECKED | C_STATIC | C_ELLIPSIS, 1, 1, 0, 8,
			 T_INT, T_STRING, T_STRING };

/*
 * NAME:	kfun->hash_crc32()
 * DESCRIPTION:	Compute a 32 bit cyclic redundancy code for a string.
 *		Based on "A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS",
 *		by Ross N. Williams.
 *
 *		    Name:	"CRC-32"	(as in libz)
 *		    Width:	16
 *		    Poly:	04C11DB7
 *		    Init:	FFFFFFFF
 *		    RefIn:	True
 *		    RefOut:	True
 *		    XorOut:	FFFFFFFF
 *		    Check:	CBF43926
 */
int kf_hash_crc32(f, nargs)
register frame *f;
int nargs;
{
    static Uint crctab[] = {
	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
	0x2d02ef8dL
    };
    register Uint crc;
    register int i;
    register ssizet len;
    register char *p;
    register Int cost;

    cost = 0;
    for (i = nargs; --i >= 0; ) {
	cost += f->sp[i].u.string->len;
    }
    cost = 3 * nargs + (cost >> 2);
    if (!f->rlim->noticks && f->rlim->ticks <= cost) {
	f->rlim->ticks = 0;
	error("Out of ticks");
    }
    i_add_ticks(f, cost);

    crc = 0xffffffff;
    for (i = nargs; --i >= 0; ) {
	p = f->sp[i].u.string->text;
	for (len = f->sp[i].u.string->len; len != 0; --len) {
	    crc = (crc >> 8) ^ crctab[UCHAR(crc ^ *p++)];
	}
	str_del(f->sp[i].u.string);
    }
    crc ^= 0xffffffffL;

    f->sp += nargs - 1;
    PUT_INTVAL(f->sp, crc);
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("hash_string", kf_hash_string, pt_hash_string)
# else
char pt_hash_string[] = { C_TYPECHECKED | C_STATIC | C_ELLIPSIS, 2, 1, 0, 9,
			  T_STRING, T_STRING, T_STRING, T_STRING };

/*
 * NAME:	hash->crypt()
 * DESCRIPTION:	hash a string with Unix password crypt
 */
static string *hash_crypt(f, passwd, salt)
register frame *f;
string *passwd, *salt;
{
    extern char *P_crypt P((char*, char*));
    static char salts[] =
	    "0123456789./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    char s[3];

    if (salt != (string *) NULL && salt->text[0] != '\0' &&
	salt->text[1] != '\0') {
	/* fixed salt */
	s[0] = salt->text[0];
	s[1] = salt->text[1];
    } else {
	Uint n;

	/* random salt */
	n = P_random();
	s[0] = salts[n & 63];
	s[1] = salts[(n >> 8) & 63];
    }
    s[2] = '\0';

    i_add_ticks(f, 900);
    return str_new(P_crypt(passwd->text, s), 13);
}

# define ROTL(x, s)			(((x) << s) | ((x) >> (32 - s)))
# define R1(a, b, c, d, Mj, s, ti)	(a += (((c ^ d) & b) ^ d) + Mj + ti, \
					 a = b + ROTL(a, s))
# define R2(a, b, c, d, Mj, s, ti)	(a += (((b ^ c) & d) ^ c) + Mj + ti, \
					 a = b + ROTL(a, s))
# define R3(a, b, c, d, Mj, s, ti)	(a += (b ^ c ^ d) + Mj + ti,	     \
					 a = b + ROTL(a, s))
# define R4(a, b, c, d, Mj, s, ti)	(a += (c ^ (b | ~d)) + Mj + ti,	     \
					 a = b + ROTL(a, s))

/*
 * NAME:	hash->md5_start()
 * DESCRIPTION:	MD5 message digest.  See "Applied Cryptography" by Bruce
 *		Schneier, Second Edition, p. 436-441.
 */
static Int hash_md5_start(f, nargs, digest)
register frame *f;
register int nargs;
register Uint *digest;
{
    register Int cost;

    /*
     * These constants must apparently be little-endianized, though AC2 does
     * not explicitly say so.
     */
    digest[0] = 0x67452301L;
    digest[1] = 0xefcdab89L;
    digest[2] = 0x98badcfeL;
    digest[3] = 0x10325476L;

    cost = 3 * nargs + 64;
    while (--nargs >= 0) {
	cost += f->sp[nargs].u.string->len;
    }
    return cost;
}

/*
 * NAME:	hash->md5_block()
 * DESCRIPTION:	add another 512 bit block to the message digest
 */
static void hash_md5_block(ABCD, block)
Uint *ABCD;
register char *block;
{
    Uint M[16];
    register int i, j;
    register Uint a, b, c, d;

    for (i = j = 0; i < 16; i++, j += 4) {
	M[i] = UCHAR(block[j + 0]) | (UCHAR(block[j + 1]) << 8) |
	       (UCHAR(block[j + 2]) << 16) | (UCHAR(block[j + 3]) << 24);

    }

    a = ABCD[0];
    b = ABCD[1];
    c = ABCD[2];
    d = ABCD[3];

    R1(a, b, c, d, M[ 0],  7, 0xd76aa478L);
    R1(d, a, b, c, M[ 1], 12, 0xe8c7b756L);
    R1(c, d, a, b, M[ 2], 17, 0x242070dbL);
    R1(b, c, d, a, M[ 3], 22, 0xc1bdceeeL);
    R1(a, b, c, d, M[ 4],  7, 0xf57c0fafL);
    R1(d, a, b, c, M[ 5], 12, 0x4787c62aL);
    R1(c, d, a, b, M[ 6], 17, 0xa8304613L);
    R1(b, c, d, a, M[ 7], 22, 0xfd469501L);
    R1(a, b, c, d, M[ 8],  7, 0x698098d8L);
    R1(d, a, b, c, M[ 9], 12, 0x8b44f7afL);
    R1(c, d, a, b, M[10], 17, 0xffff5bb1L);
    R1(b, c, d, a, M[11], 22, 0x895cd7beL);
    R1(a, b, c, d, M[12],  7, 0x6b901122L);
    R1(d, a, b, c, M[13], 12, 0xfd987193L);
    R1(c, d, a, b, M[14], 17, 0xa679438eL);
    R1(b, c, d, a, M[15], 22, 0x49b40821L);

    R2(a, b, c, d, M[ 1],  5, 0xf61e2562L);
    R2(d, a, b, c, M[ 6],  9, 0xc040b340L);
    R2(c, d, a, b, M[11], 14, 0x265e5a51L);
    R2(b, c, d, a, M[ 0], 20, 0xe9b6c7aaL);
    R2(a, b, c, d, M[ 5],  5, 0xd62f105dL);
    R2(d, a, b, c, M[10],  9, 0x02441453L);
    R2(c, d, a, b, M[15], 14, 0xd8a1e681L);
    R2(b, c, d, a, M[ 4], 20, 0xe7d3fbc8L);
    R2(a, b, c, d, M[ 9],  5, 0x21e1cde6L);
    R2(d, a, b, c, M[14],  9, 0xc33707d6L);
    R2(c, d, a, b, M[ 3], 14, 0xf4d50d87L);
    R2(b, c, d, a, M[ 8], 20, 0x455a14edL);
    R2(a, b, c, d, M[13],  5, 0xa9e3e905L);
    R2(d, a, b, c, M[ 2],  9, 0xfcefa3f8L);
    R2(c, d, a, b, M[ 7], 14, 0x676f02d9L);
    R2(b, c, d, a, M[12], 20, 0x8d2a4c8aL);

    R3(a, b, c, d, M[ 5],  4, 0xfffa3942L);
    R3(d, a, b, c, M[ 8], 11, 0x8771f681L);
    R3(c, d, a, b, M[11], 16, 0x6d9d6122L);
    R3(b, c, d, a, M[14], 23, 0xfde5380cL);
    R3(a, b, c, d, M[ 1],  4, 0xa4beea44L);
    R3(d, a, b, c, M[ 4], 11, 0x4bdecfa9L);
    R3(c, d, a, b, M[ 7], 16, 0xf6bb4b60L);
    R3(b, c, d, a, M[10], 23, 0xbebfbc70L);
    R3(a, b, c, d, M[13],  4, 0x289b7ec6L);
    R3(d, a, b, c, M[ 0], 11, 0xeaa127faL);
    R3(c, d, a, b, M[ 3], 16, 0xd4ef3085L);
    R3(b, c, d, a, M[ 6], 23, 0x04881d05L);
    R3(a, b, c, d, M[ 9],  4, 0xd9d4d039L);
    R3(d, a, b, c, M[12], 11, 0xe6db99e5L);
    R3(c, d, a, b, M[15], 16, 0x1fa27cf8L);
    R3(b, c, d, a, M[ 2], 23, 0xc4ac5665L);

    R4(a, b, c, d, M[ 0],  6, 0xf4292244L);
    R4(d, a, b, c, M[ 7], 10, 0x432aff97L);
    R4(c, d, a, b, M[14], 15, 0xab9423a7L);
    R4(b, c, d, a, M[ 5], 21, 0xfc93a039L);
    R4(a, b, c, d, M[12],  6, 0x655b59c3L);
    R4(d, a, b, c, M[ 3], 10, 0x8f0ccc92L);
    R4(c, d, a, b, M[10], 15, 0xffeff47dL);
    R4(b, c, d, a, M[ 1], 21, 0x85845dd1L);
    R4(a, b, c, d, M[ 8],  6, 0x6fa87e4fL);
    R4(d, a, b, c, M[15], 10, 0xfe2ce6e0L);
    R4(c, d, a, b, M[ 6], 15, 0xa3014314L);
    R4(b, c, d, a, M[13], 21, 0x4e0811a1L);
    R4(a, b, c, d, M[ 4],  6, 0xf7537e82L);
    R4(d, a, b, c, M[11], 10, 0xbd3af235L);
    R4(c, d, a, b, M[ 2], 15, 0x2ad7d2bbL);
    R4(b, c, d, a, M[ 9], 21, 0xeb86d391L);

    ABCD[0] += a;
    ABCD[1] += b;
    ABCD[2] += c;
    ABCD[3] += d;
}

/*
 * NAME:	hash->md5_end()
 * DESCRIPTION:	finish up MD5 hash
 */
static string *hash_md5_end(digest, buffer, bufsz, length)
register Uint *digest;
register char *buffer;
register unsigned int bufsz;
register Uint length;
{
    register int i;

    /* append padding and digest final block(s) */
    buffer[bufsz++] = 0x80;
    if (bufsz > 56) {
	memset(buffer + bufsz, '\0', 64 - bufsz);
	hash_md5_block(digest, buffer);
	bufsz = 0;
    }
    memset(buffer + bufsz, '\0', 64 - bufsz);
    buffer[56] = length << 3;
    buffer[57] = length >> 5;
    buffer[58] = length >> 13;
    buffer[59] = length >> 21;
    buffer[60] = length >> 29;
    hash_md5_block(digest, buffer);

    for (bufsz = i = 0; i < 4; bufsz += 4, i++) {
	buffer[bufsz + 0] = digest[i];
	buffer[bufsz + 1] = digest[i] >> 8;
	buffer[bufsz + 2] = digest[i] >> 16;
	buffer[bufsz + 3] = digest[i] >> 24;
    }
    return str_new(buffer, 16L);
}

/*
 * NAME:	hash->sha1_start()
 * DESCRIPTION:	SHA-1 message digest.  See FIPS 180-2.
 */
static Int hash_sha1_start(f, nargs, digest)
register frame *f;
register int nargs;
register Uint *digest;
{
    register Int cost;

    digest[0] = 0x67452301L;
    digest[1] = 0xefcdab89L;
    digest[2] = 0x98badcfeL;
    digest[3] = 0x10325476L;
    digest[4] = 0xc3d2e1f0L;

    cost = 3 * nargs + 64;
    while (--nargs >= 0) {
	cost += f->sp[nargs].u.string->len;
    }
    return cost;
}

/*
 * NAME:	hash->sha1_block()
 * DESCRIPTION:	add another 512 bit block to the message digest
 */
static void hash_sha1_block(ABCDE, block)
Uint *ABCDE;
register char *block;
{
    Uint W[80];
    register int i, j;
    register Uint a, b, c, d, e, t;

    for (i = j = 0; i < 16; i++, j += 4) {
       W[i] = (UCHAR(block[j + 0]) << 24) | (UCHAR(block[j + 1]) << 16) |
	      (UCHAR(block[j + 2]) << 8) | UCHAR(block[j + 3]);

    }
    while (i < 80) {
	W[i] = ROTL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
	i++;
    }

    a = ABCDE[0];
    b = ABCDE[1];
    c = ABCDE[2];
    d = ABCDE[3];
    e = ABCDE[4];

    for (i = 0; i < 20; i++) {
	t = ROTL(a, 5) + (((c ^ d) & b) ^ d) + e + W[i] + 0x5a827999L;
	e = d;
	d = c;
	c = ROTL(b, 30);
	b = a;
	a = t;
    }
    while (i < 40) {
	t = ROTL(a, 5) + (b ^ c ^ d) + e + W[i] + 0x6ed9eba1L;
	e = d;
	d = c;
	c = ROTL(b, 30);
	b = a;
	a = t;
	i++;
    }
    while (i < 60) {
	t = ROTL(a, 5) + ((b & c) | ((b | c) & d)) + e + W[i] + 0x8f1bbcdcL;
	e = d;
	d = c;
	c = ROTL(b, 30);
	b = a;
	a = t;
	i++;
    }
    while (i < 80) {
	t = ROTL(a, 5) + (b ^ c ^ d) + e + W[i] + 0xca62c1d6L;
	e = d;
	d = c;
	c = ROTL(b, 30);
	b = a;
	a = t;
	i++;
    }

    ABCDE[0] += a;
    ABCDE[1] += b;
    ABCDE[2] += c;
    ABCDE[3] += d;
    ABCDE[4] += e;
}


/*
 * NAME:	hash->sha1_end()
 * DESCRIPTION:	finish up SHA-1 hash
 */
static string *hash_sha1_end(digest, buffer, bufsz, length)
register Uint *digest, length;
register char *buffer;
register unsigned int bufsz;
{
    register int i;

    /* append padding and digest final block(s) */
    buffer[bufsz++] = 0x80;
    if (bufsz > 56) {
	memset(buffer + bufsz, '\0', 64 - bufsz);
	hash_sha1_block(digest, buffer);
	bufsz = 0;
    }
    memset(buffer + bufsz, '\0', 64 - bufsz);
    buffer[59] = length >> 29;
    buffer[60] = length >> 21;
    buffer[61] = length >> 13;
    buffer[62] = length >> 5;
    buffer[63] = length << 3;
    hash_sha1_block(digest, buffer);

    for (bufsz = i = 0; i < 5; bufsz += 4, i++) {
	buffer[bufsz + 0] = digest[i] >> 24;
	buffer[bufsz + 1] = digest[i] >> 16;
	buffer[bufsz + 2] = digest[i] >> 8;
	buffer[bufsz + 3] = digest[i];
    }
    return str_new(buffer, 20L);
}

/*
 * NAME:	hash->blocks()
 * DESCRIPTION:	hash string blocks with a given function
 */
static Uint hash_blocks(f, nargs, digest, buffer, bufsize, blocksz, hash_block)
register frame *f;
register int nargs;
Uint *digest;
char *buffer;
unsigned short *bufsize;
register unsigned int blocksz;
void (*hash_block) P((Uint*, char*));
{
    register ssizet len;
    register unsigned short bufsz;
    register char *p;
    register Uint length;

    length = 0;
    bufsz = 0;
    while (--nargs >= 0) {
	len = f->sp[nargs].u.string->len;
	if (len != 0) {
	    length += len;
	    p = f->sp[nargs].u.string->text;
	    if (bufsz != 0) {
		register unsigned short size;

		/* fill buffer and digest */
		size = blocksz - bufsz;
		if (size > len) {
		    size = len;
		}
		memcpy(buffer + bufsz, p, size);
		p += size;
		len -= size;
		bufsz += size;

		if (bufsz == blocksz) {
		    (*hash_block)(digest, buffer);
		    bufsz = 0;
		}
	    }

	    while (len >= blocksz) {
		/* digest directly from string */
		(*hash_block)(digest, p);
		p += blocksz;
		len -= blocksz;
	    }

	    if (len != 0) {
		/* put remainder in buffer */
		memcpy(buffer, p, bufsz = len);
	    }
	}
	str_del(f->sp[nargs].u.string);
    }

    *bufsize = bufsz;
    return length;
}

/*
 * NAME:	kfun->hash_string()
 * DESCRIPTION:	hash a string
 */
int kf_hash_string(f, nargs)
register frame *f;
int nargs;
{
    register string *str, *salt;
    char buffer[64];
    Uint digest[5];
    Int cost;
    Uint length;
    unsigned short bufsz;

    str = f->sp[nargs - 1].u.string;
    switch (str->text[0]) {
    case 'c':
	if (str->len == 5 && strcmp(str->text, "crypt") == 0) {
	    if (nargs > 3) {
		return 3;
	    }
	    if (nargs == 3) {
		salt = f->sp->u.string;
		f->sp++;
	    } else {
		salt = (string *) NULL;
	    }
	    str = hash_crypt(f, f->sp->u.string, salt);
	    if (salt != (string *) NULL) {
		str_del(salt);
	    }
	    str_del((f->sp++)->u.string);
	    str_del(f->sp->u.string);
	    PUT_STR(f->sp, str);
	    return 0;
	}
	break;

    case 'M':
	if (str->len == 3 && strcmp(str->text, "MD5") == 0) {
	    cost = hash_md5_start(f, nargs, digest);
	    if (!f->rlim->noticks && f->rlim->ticks <= cost) {
		f->rlim->ticks = 0;
		error("Out of ticks");
	    }
	    i_add_ticks(f, cost);

	    length = hash_blocks(f, --nargs, digest, buffer, &bufsz, 64,
				 &hash_md5_block);

	    f->sp += nargs;
	    str_del(f->sp->u.string);
	    PUT_STR(f->sp, hash_md5_end(digest, buffer, bufsz, length));
	    return 0;
	}
	break;

    case 'S':
	if (str->len == 4 && strcmp(str->text, "SHA1") == 0) {
	    cost = hash_sha1_start(f, nargs, digest);
	    if (!f->rlim->noticks && f->rlim->ticks <= cost) {
		f->rlim->ticks = 0;
		error("Out of ticks");
	    }
	    i_add_ticks(f, cost);

	    length = hash_blocks(f, --nargs, digest, buffer, &bufsz, 64,
				 &hash_sha1_block);

	    f->sp += nargs;
	    str_del(f->sp->u.string);
	    PUT_STR(f->sp, hash_sha1_end(digest, buffer, bufsz, length));
	    return 0;
	}
	break;
    }

    error("Unknown hash algorithm");
}
# endif


# ifdef FUNCDEF
FUNCDEF("crypt", kf_crypt, pt_crypt)
# else
char pt_crypt[] = { C_TYPECHECKED | C_STATIC, 1, 1, 0, 8, T_STRING, T_STRING,
		    T_STRING };

/*
 * NAME:	kfun->crypt()
 * DESCRIPTION:	hash_string("crypt", ...)
 */
int kf_crypt(f, nargs)
register frame *f;
int nargs;
{
    string *salt, *str;

    if (nargs == 2) {
	salt = f->sp->u.string;
	f->sp++;
    } else {
	salt = (string *) NULL;
    }
    str = hash_crypt(f, f->sp->u.string, salt);
    if (salt != (string *) NULL) {
	str_del(salt);
    }
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("hash_md5", kf_hash_md5, pt_hash_md5)
# else
char pt_hash_md5[] = { C_TYPECHECKED | C_STATIC | C_ELLIPSIS, 1, 1, 0, 8,
		       T_STRING, T_STRING, T_STRING };

/*
 * NAME:	kfun->hash_md5()
 * DESCRIPTION:	hash_string("MD5", ...)
 */
int kf_hash_md5(f, nargs)
register frame *f;
int nargs;
{
    char buffer[64];
    Uint digest[4];
    Int cost;
    Uint length;
    unsigned short bufsz;

    cost = hash_md5_start(f, nargs, digest);
    if (!f->rlim->noticks && f->rlim->ticks <= cost) {
	f->rlim->ticks = 0;
	error("Out of ticks");
    }
    i_add_ticks(f, cost);

    length = hash_blocks(f, nargs, digest, buffer, &bufsz, 64, &hash_md5_block);

    f->sp += nargs - 1;
    PUT_STR(f->sp, hash_md5_end(digest, buffer, bufsz, length));
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("hash_sha1", kf_hash_sha1, pt_hash_sha1)
# else
char pt_hash_sha1[] = { C_TYPECHECKED | C_STATIC | C_ELLIPSIS, 1, 1, 0, 8,
			T_STRING, T_STRING, T_STRING };

/*
 * NAME:	kfun->hash_sha1()
 * DESCRIPTION:	hash_string("SHA1", ...)
 */
int kf_hash_sha1(f, nargs)
register frame *f;
int nargs;
{
    char buffer[64];
    Uint digest[5];
    Int cost;
    Uint length;
    unsigned short bufsz;

    cost = hash_sha1_start(f, nargs, digest);
    if (!f->rlim->noticks && f->rlim->ticks <= cost) {
	f->rlim->ticks = 0;
	error("Out of ticks");
    }
    i_add_ticks(f, cost);

    length = hash_blocks(f, nargs, digest, buffer, &bufsz, 64,
			 &hash_sha1_block);

    f->sp += nargs - 1;
    PUT_STR(f->sp, hash_sha1_end(digest, buffer, bufsz, length));
    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_add", kf_asn_add, pt_asn_add)
# else
char pt_asn_add[] = { C_TYPECHECKED | C_STATIC, 3, 0, 0, 9, T_STRING, T_STRING,
		      T_STRING, T_STRING };

/*
 * NAME:	kfun->asn_add()
 * DESCRIPTION:	add two arbitrary precision numbers
 */
int kf_asn_add(f)
register frame *f;
{
    register string *str;

    str = asn_add(f, f->sp[2].u.string, f->sp[1].u.string, f->sp[0].u.string);
    str_del((f->sp++)->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_sub", kf_asn_sub, pt_asn_sub)
# else
char pt_asn_sub[] = { C_TYPECHECKED | C_STATIC, 3, 0, 0, 9, T_STRING, T_STRING,
		      T_STRING, T_STRING };

/*
 * NAME:	kfun->asn_sub()
 * DESCRIPTION:	subtract arbitrary precision numbers
 */
int kf_asn_sub(f)
register frame *f;
{
    register string *str;

    str = asn_sub(f, f->sp[2].u.string, f->sp[1].u.string, f->sp[0].u.string);
    str_del((f->sp++)->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_cmp", kf_asn_cmp, pt_asn_cmp)
# else
char pt_asn_cmp[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8, T_INT, T_STRING,
		      T_STRING };

/*
 * NAME:	kfun->asn_cmp()
 * DESCRIPTION:	subtract arbitrary precision numbers
 */
int kf_asn_cmp(f)
register frame *f;
{
    int cmp;

    cmp = asn_cmp(f, f->sp[1].u.string, f->sp[0].u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_INTVAL(f->sp, cmp);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_mult", kf_asn_mult, pt_asn_mult)
# else
char pt_asn_mult[] = { C_TYPECHECKED | C_STATIC, 3, 0, 0, 9, T_STRING, T_STRING,
		       T_STRING, T_STRING };

/*
 * NAME:	kfun->asn_mult()
 * DESCRIPTION:	multiply arbitrary precision numbers
 */
int kf_asn_mult(f)
register frame *f;
{
    register string *str;

    str = asn_mult(f, f->sp[2].u.string, f->sp[1].u.string, f->sp[0].u.string);
    str_del((f->sp++)->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_div", kf_asn_div, pt_asn_div)
# else
char pt_asn_div[] = { C_TYPECHECKED | C_STATIC, 3, 0, 0, 9, T_STRING, T_STRING,
		      T_STRING, T_STRING };

/*
 * NAME:	kfun->asn_div()
 * DESCRIPTION:	divide arbitrary precision numbers
 */
int kf_asn_div(f)
register frame *f;
{
    register string *str;

    str = asn_div(f, f->sp[2].u.string, f->sp[1].u.string, f->sp[0].u.string);
    str_del((f->sp++)->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_mod", kf_asn_mod, pt_asn_mod)
# else
char pt_asn_mod[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8, T_STRING, T_STRING,
		      T_STRING };

/*
 * NAME:	kfun->asn_mod()
 * DESCRIPTION:	modulus of arbitrary precision number
 */
int kf_asn_mod(f)
register frame *f;
{
    register string *str;

    str = asn_mod(f, f->sp[1].u.string, f->sp[0].u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_pow", kf_asn_pow, pt_asn_pow)
# else
char pt_asn_pow[] = { C_TYPECHECKED | C_STATIC, 3, 0, 0, 9, T_STRING, T_STRING,
		      T_STRING, T_STRING };

/*
 * NAME:	kfun->asn_pow()
 * DESCRIPTION:	power of an arbitrary precision number
 */
int kf_asn_pow(f)
register frame *f;
{
    register string *str;

    str = asn_pow(f, f->sp[2].u.string, f->sp[1].u.string, f->sp[0].u.string);
    str_del((f->sp++)->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_lshift", kf_asn_lshift, pt_asn_lshift)
# else
char pt_asn_lshift[] = { C_TYPECHECKED | C_STATIC, 3, 0, 0, 9, T_STRING,
			 T_STRING, T_INT, T_STRING };

/*
 * NAME:	kfun->asn_lshift()
 * DESCRIPTION:	left shift an arbitrary precision number
 */
int kf_asn_lshift(f)
register frame *f;
{
    string *str;

    str = asn_lshift(f, f->sp[2].u.string, f->sp[1].u.number, f->sp->u.string);
    str_del(f->sp->u.string);
    f->sp += 2;
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_rshift", kf_asn_rshift, pt_asn_rshift)
# else
char pt_asn_rshift[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8, T_STRING,
			 T_STRING, T_INT };

/*
 * NAME:	kfun->asn_rshift()
 * DESCRIPTION:	right shift of arbitrary precision number
 */
int kf_asn_rshift(f)
register frame *f;
{
    string *str;

    str = asn_rshift(f, f->sp[1].u.string, f->sp->u.number);
    f->sp++;
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_and", kf_asn_and, pt_asn_and)
# else
char pt_asn_and[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8, T_STRING, T_STRING,
		      T_STRING };

/*
 * NAME:	kfun->asn_and()
 * DESCRIPTION:	logical and of arbitrary precision numbers
 */
int kf_asn_and(f)
register frame *f;
{
    string *str;

    str = asn_and(f, f->sp[1].u.string, f->sp->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_or", kf_asn_or, pt_asn_or)
# else
char pt_asn_or[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8, T_STRING, T_STRING,
		     T_STRING };

/*
 * NAME:	kfun->asn_or()
 * DESCRIPTION:	logical or of arbitrary precision numbers
 */
int kf_asn_or(f)
register frame *f;
{
    string *str;

    str = asn_or(f, f->sp[1].u.string, f->sp->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif


# ifdef FUNCDEF
FUNCDEF("asn_xor", kf_asn_xor, pt_asn_xor)
# else
char pt_asn_xor[] = { C_TYPECHECKED | C_STATIC, 2, 0, 0, 8, T_STRING, T_STRING,
		      T_STRING };

/*
 * NAME:	kfun->asn_xor()
 * DESCRIPTION:	logical xor of arbitrary precision numbers
 */
int kf_asn_xor(f)
register frame *f;
{
    string *str;

    str = asn_xor(f, f->sp[1].u.string, f->sp->u.string);
    str_del((f->sp++)->u.string);
    str_del(f->sp->u.string);
    PUT_STR(f->sp, str);

    return 0;
}
# endif
