/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1996-2011 AT&T Intellectual Property          *
*          Copyright (c) 2020-2024 Contributors to ksh 93u+m           *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 2.0                  *
*                                                                      *
*                A copy of the License is available at                 *
*      https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html      *
*         (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d)         *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  Martijn Dekker <martijn@inlv.org>                   *
*            Johnothan King <johnothanking@protonmail.com>             *
*                                                                      *
***********************************************************************/
/*
 * Glenn Fowler
 * AT&T Research
 *
 * man this is sum library
 */

#define _SUM_PRIVATE_	\
			struct Method_s*	method;	\
			uintmax_t		total_count;	\
			uintmax_t		total_size;	\
			uintmax_t		size;

#include <sum.h>
#include <ctype.h>
#include <swap.h>
#include <hashpart.h>

#define SCALE(n,m)	(((n)+(m)-1)/(m))

typedef struct Method_s
{
	const char*	match;
	const char*	description;
	const char*	options;
	Sum_t*		(*open)(const struct Method_s*, const char*);
	int		(*init)(Sum_t*);
	int		(*block)(Sum_t*, const void*, size_t);
	int		(*data)(Sum_t*, Sumdata_t*);
	int		(*print)(Sum_t*, Sfio_t*, int, size_t);
	int		(*done)(Sum_t*);
	int		scale;
} Method_t;

typedef struct Map_s
{
	const char*	match;
	const char*	description;
	const char*	map;
} Map_t;

/*
 * 16 and 32 bit common code
 */

#define _INTEGRAL_PRIVATE_ \
	uint32_t	sum; \
	uint32_t	total_sum;
	
typedef struct Integral_s
{
	_SUM_PUBLIC_
	_SUM_PRIVATE_
	_INTEGRAL_PRIVATE_
} Integral_t;

static Sum_t*
long_open(const Method_t* method, const char* name)
{
	Integral_t*	p;

	if (p = newof(0, Integral_t, 1, 0))
	{
		p->method = (Method_t*)method;
		p->name = name;
	}
	return (Sum_t*)p;
}

static int
long_init(Sum_t* p)
{
	((Integral_t*)p)->sum = 0;
	return 0;
}

static int
long_done(Sum_t* p)
{
	Integral_t*	x = (Integral_t*)p;

	x->total_sum ^= (x->sum &= 0xffffffff);
	return 0;
}

static int
short_done(Sum_t* p)
{
	Integral_t*	x = (Integral_t*)p;

	x->total_sum ^= (x->sum &= 0xffff);
	return 0;
}

static int
long_print(Sum_t* p, Sfio_t* sp, int flags, size_t scale)
{
	Integral_t*	x = (Integral_t*)p;
	uint32_t	c;
	uintmax_t	z;
	size_t		n;

	c = (flags & SUM_TOTAL) ? x->total_sum : x->sum;
	sfprintf(sp, "%.*I*u", (flags & SUM_LEGACY) ? 5 : 1, sizeof(c), c);
	if (flags & SUM_SIZE)
	{
		z = (flags & SUM_TOTAL) ? x->total_size : x->size;
		if ((flags & SUM_SCALE) && ((n = scale) || (n = x->method->scale)))
			z = SCALE(z, n);
		sfprintf(sp, " %*I*u", (flags & SUM_LEGACY) ? 6 : 0, sizeof(z), z);
	}
	if (flags & SUM_TOTAL)
		sfprintf(sp, " %*I*u", (flags & SUM_LEGACY) ? 6 : 0, sizeof(x->total_count), x->total_count);
	return 0;
}

static int
long_data(Sum_t* p, Sumdata_t* data)
{
	Integral_t*	x = (Integral_t*)p;

	data->size = sizeof(data->num);
	data->num = x->sum;
	data->buf = 0;
	return 0;
}

#include "FEATURE/sum"

#include "sum-att.c"
#include "sum-ast4.c"
#include "sum-bsd.c"
#include "sum-crc.c"
#include "sum-prng.c"

#if _LIB_md && _lib_MD5Init && _hdr_md5 && _lib_SHA2Init && _hdr_sha2

#include "sum-lmd.c"

#else

#include "sum-md5.c"
#include "sum-sha1.c"
#include "sum-sha2.c"

#endif

/*
 * now the library interface
 */

#undef	METHOD		/* Solaris <sys/localedef.h>! */
#define METHOD(x)	x##_match,x##_description,x##_options,x##_open,x##_init,x##_block,x##_data,x##_print,x##_done,x##_scale

static const Method_t	methods[] =
{
	METHOD(att),
	METHOD(ast4),
	METHOD(bsd),
	METHOD(crc),
	METHOD(prng),
#ifdef md4_description
	METHOD(md4),
#endif
#ifdef md5_description
	METHOD(md5),
#endif
#ifdef sha1_description
	METHOD(sha1),
#endif
#ifdef sha256_description
	METHOD(sha256),
#endif
#ifdef sha384_description
	METHOD(sha384),
#endif
#ifdef sha512_description
	METHOD(sha512),
#endif
};

static const Map_t	maps[] =
{
	{
		"posix|cksum|std|standard",
		"The POSIX 1003.2-1992 32 bit crc checksum. This is the"
		" default \bcksum\b(1)  method.",
		"crc-0x04c11db7-rotate-done-size"
	},
	{
		"zip",
		"The \bzip\b(1) crc.",
		"crc-0xedb88320-init-done"
	},
	{
		"fddi",
		"The FDDI crc.",
		"crc-0xedb88320-size=0xcc55cc55"
	},
	{
		"fnv|fnv1",
		"The Fowler-Noll-Vo 32 bit PRNG hash with non-zero"
		" initializer (FNV-1).",
		"prng-0x01000193-init=0x811c9dc5"
	},
	{
		"ast|strsum",
		"The \bAST\b \bstrsum\b(3) PRNG hash.",
		"prng-0x63c63cd9-add=0x9c39c33d"
	},
};

/*
 * simple alternation prefix match
 */

static int
match(const char* s, const char* p)
{
	const char*	b = s;

	for (;;)
	{
		do
		{
			if (*p == '|' || *p == 0)
				return 1;
		} while (*s++ == *p++);
		for (;;)
		{
			switch (*p++)
			{
			case 0:
				return 0;
			case '|':
				break;
			default:
				continue;
			}
			break;
		}
		s = b;
	}
	return 0;
}

/*
 * open sum method name
 */

Sum_t*
sumopen(const char* name)
{
	int	n;

	if (!name || !name[0] || name[0] == '-' && !name[1])
		name = "default";
	for (n = 0; n < elementsof(maps); n++)
		if (match(name, maps[n].match))
		{
			name = maps[n].map;
			break;
		}
	for (n = 0; n < elementsof(methods); n++)
		if (match(name, methods[n].match))
			return (*methods[n].open)(&methods[n], name);
	return NULL;
}

/*
 * initialize for a new run of blocks
 */

int
suminit(Sum_t* p)
{
	p->size = 0;
	return (*p->method->init)(p);
}

/*
 * compute the running sum on buf
 */

int
sumblock(Sum_t* p, const void* buf, size_t siz)
{
	p->size += siz;
	return (*p->method->block)(p, buf, siz);
}

/*
 * done with this run of blocks
 */

int
sumdone(Sum_t* p)
{
	p->total_count++;
	p->total_size += p->size;
	return (*p->method->done)(p);
}

/*
 * print the sum [size] on sp
 */

int
sumprint(Sum_t* p, Sfio_t* sp, int flags, size_t scale)
{
	return (*p->method->print)(p, sp, flags, scale);
}

/*
 * return the current sum (internal) data
 */

int
sumdata(Sum_t* p, Sumdata_t* d)
{
	return (*p->method->data)(p, d);
}

/*
 * close an open sum handle
 */

int
sumclose(Sum_t* p)
{
	free(p);
	return 0;
}

/*
 * print the checksum method optget(3) usage on sp and return the length
 */

int
sumusage(Sfio_t* sp)
{
	int	i;
	int	n;

	for (i = n = 0; i < elementsof(methods); i++)
	{
		n += sfprintf(sp, "[+%s?%s]", methods[i].match, methods[i].description);
		if (methods[i].options)
			n += sfprintf(sp, "{\n%s\n}", methods[i].options);
	}
	for (i = 0; i < elementsof(maps); i++)
		n += sfprintf(sp, "[+%s?%s Shorthand for \b%s\b.]", maps[i].match, maps[i].description, maps[i].map);
	return n;
}
