/* string.c
 * (c) 2002 Mikulas Patocka
 * This file is a part of the Links program, released under GPL.
 */

#include "links.h"

int snprint(unsigned char *s, int n, my_uintptr_t num)
{
	my_uintptr_t q = 1;
	while (q <= num / 10) q *= 10;
	while (n-- > 1 && q) {
		*(s++) = (unsigned char)(num / q + '0');
		num %= q;
#ifdef __DECC_VER
		do_not_optimize_here(&q);	/* compiler bug */
#endif
		q /= 10;
	}
	*s = 0;
	return !!q;
}

int snzprint(unsigned char *s, int n, off_t num)
{
	off_t q = 1;
	if (n > 1 && num < 0) *(s++) = '-', num = -num, n--;
	while (q <= num / 10) q *= 10;
	while (n-- > 1 && q) {
		*(s++) = (unsigned char)(num / q + '0');
		num %= q;
#ifdef __DECC_VER
		do_not_optimize_here(&q);	/* compiler bug */
#endif
		q /= 10;
	}
	*s = 0;
	return !!q;
}

void add_to_strn(unsigned char **s, unsigned char *a)
{
	unsigned char *p;
	size_t l1 = strlen(cast_const_char *s), l2 = strlen(cast_const_char a);
	if (((l1 | l2) | (l1 + l2 + 1)) > MAXINT) overalloc();
	p = (unsigned char *)mem_realloc(*s, l1 + l2 + 1);
	strcat(cast_char p, cast_const_char a);
	*s = p;
}

void extend_str(unsigned char **s, int n)
{
	size_t l = strlen(cast_const_char *s);
	if (((l | n) | (l + n + 1)) > MAXINT) overalloc();
	*s = (unsigned char *)mem_realloc(*s, l + n + 1);
}

void add_bytes_to_str(unsigned char **s, int *l, unsigned char *a, size_t ll)
{
	unsigned char *p;
	size_t old_length;
	size_t new_length;
	size_t x;

	if (!ll)
		return;

	p = *s;
	old_length = (unsigned)*l;
	if (ll + old_length >= (unsigned)MAXINT / 2 || ll + old_length < ll) overalloc();
	new_length = old_length + ll;
	*l = (int)new_length;
	x = old_length ^ new_length;
	if (x >= old_length) {
		/* Need to realloc */
#ifdef HAVE___BUILTIN_CLZ
#if !(									\
	defined(__tune_i386__) ||					\
	defined(__tune_i486__) ||					\
	defined(__tune_i586__) ||					\
	defined(__tune_k6__) ||						\
	defined(__tune_lakemont__) ||					\
	(defined(__alpha__) && !defined(__alpha_cix__)) ||		\
	(defined(__mips) && __mips < 32) ||				\
	(defined(__ARM_ARCH) && __ARM_ARCH < 5) ||			\
	(defined(__sparc__) && (!defined(__VIS__) || __VIS__ < 0x300)) ||\
	defined(__hppa) ||						\
	(defined(__riscv) && !defined(__riscv_zbb)) ||			\
	defined(__sh__))
		if (!(sizeof(unsigned) & (sizeof(unsigned) - 1))) {
			new_length = 2U << ((sizeof(unsigned) * 8 - 1)
#ifdef __ICC
				-
#else
				^
#endif
				__builtin_clz(new_length));
		} else
#endif
#endif
		{
			new_length |= new_length >> 1;
			new_length |= new_length >> 2;
			new_length |= new_length >> 4;
			new_length |= new_length >> 8;
			new_length |= new_length >> 16;
			new_length++;
		}
		p = (unsigned char *)mem_realloc(p, new_length);
		*s = p;
	}
	p[*l] = 0;
	memcpy(p + old_length, a, ll);
}

void add_to_str(unsigned char **s, int *l, unsigned char *a)
{
	add_bytes_to_str(s, l, a, strlen(cast_const_char a));
}

void add_chr_to_str(unsigned char **s, int *l, unsigned char a)
{
	add_bytes_to_str(s, l, &a, 1);
}

void add_unsigned_long_num_to_str(unsigned char **s, int *l, my_uintptr_t n)
{
	unsigned char a[64];
	snprint(a, 64, n);
	add_to_str(s, l, a);
}

void add_num_to_str(unsigned char **s, int *l, off_t n)
{
	unsigned char a[64];
	if (n >= 0 && n < 1000) {
		unsigned sn = (unsigned)n;
		unsigned char *p = a;
		if (sn >= 100) {
			*p++ = '0' + sn / 100;
			sn %= 100;
			goto d10;
		}
		if (sn >= 10) {
			d10:
			*p++ = '0' + sn / 10;
			sn %= 10;
		}
		*p++ = '0' + sn;
		add_bytes_to_str(s, l, a, p - a);
	} else {
		snzprint(a, 64, n);
		add_to_str(s, l, a);
	}
}

void add_knum_to_str(unsigned char **s, int *l, off_t n)
{
	unsigned char a[13];
	if (n && n / (1024 * 1024) * (1024 * 1024) == n) snzprint(a, 12, n / (1024 * 1024)), a[strlen(cast_const_char a) + 1] = 0, a[strlen(cast_const_char a)] = 'M';
	else if (n && n / 1024 * 1024 == n) snzprint(a, 12, n / 1024), a[strlen(cast_const_char a) + 1] = 0, a[strlen(cast_const_char a)] = 'k';
	else snzprint(a, 13, n);
	add_to_str(s, l, a);
}

long strtolx(unsigned char *c, unsigned char **end)
{
	char *end_c;
	long l;
	if (c[0] == '0' && upcase(c[1]) == 'X' && c[2]) l = strtol(cast_const_char(c + 2), &end_c, 16);
	else l = strtol(cast_const_char c, &end_c, 10);
	*end = cast_uchar end_c;
	if (upcase(**end) == 'K') {
		(*end)++;
		if (l < -MAXINT / 1024) return -MAXINT;
		if (l > MAXINT / 1024) return MAXINT;
		return l * 1024;
	}
	if (upcase(**end) == 'M') {
		(*end)++;
		if (l < -MAXINT / (1024 * 1024)) return -MAXINT;
		if (l > MAXINT / (1024 * 1024)) return MAXINT;
		return l * (1024 * 1024);
	}
	return l;
}

my_strtoll_t my_strtoll(unsigned char *string, unsigned char **end)
{
	char *end_c;
	my_strtoll_t f;
	errno = 0;
#if defined(HAVE_STRTOLL)
	f = strtoll(cast_const_char string, &end_c, 10);
#elif defined(HAVE_STRTOQ)
	f = strtoq(cast_const_char string, &end_c, 10);
#elif defined(HAVE_STRTOIMAX)
	f = strtoimax(cast_const_char string, &end_c, 10);
#else
	f = strtol(cast_const_char string, &end_c, 10);
#endif
	if (end)
		*end = cast_uchar end_c;
	if (f < 0 || errno) return -1;
	return f;
}

/* Copies at most dst_size chars into dst. Ensures null termination of dst. */
void safe_strncpy(unsigned char *dst, const unsigned char *src, size_t dst_size)
{
	dst[dst_size - 1] = 0;
	strncpy(cast_char dst, cast_const_char src, dst_size - 1);
}

#ifdef JS
/* deletes all nonprintable characters from string */
void skip_nonprintable(unsigned char *txt)
{
	unsigned char *txt1=txt;

	if (!txt||!*txt)return;
	for (;*txt;txt++)
		switch(*txt)
		{
			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
			case 11:
			case 12:
			case 13:
			case 14:
			case 15:
			case 16:
			case 17:
			case 18:
			case 19:
			case 20:
			case 21:
			case 22:
			case 23:
			case 24:
			case 25:
			case 26:
			case 27:
			case 28:
			case 29:
			case 30:
			case 31:
			break;

			case 9:
			*txt1=' ';
			txt1++;
			break;

			default:
			*txt1=*txt;
			txt1++;
			break;
		}
	*txt1=0;
}
#endif

/* don't use strcasecmp because it depends on locale */
int casestrcmp(const unsigned char *s1, const unsigned char *s2)
{
	while (1) {
		unsigned char c1 = *s1;
		unsigned char c2 = *s2;
		c1 = locase(c1);
		c2 = locase(c2);
		if (c1 != c2) {
			return (int)c1 - (int)c2;
		}
		if (!*s1) break;
		s1++, s2++;
	}
	return 0;
}

/* case insensitive compare of 2 strings */
/* comparison ends after len (or less) characters */
/* return value: 1=strings differ, 0=strings are same */
int casecmp(const unsigned char *c1, const unsigned char *c2, size_t len)
{
	size_t i;
	for (i = 0; i < len; i++) if (srch_cmp(c1[i], c2[i])) return 1;
	return 0;
}

int casestrstr(const unsigned char *h, const unsigned char *n)
{
	const unsigned char *p;

	for (p=h;*p;p++)
	{
		if (!srch_cmp(*p,*n))  /* same */
		{
			const unsigned char *q, *r;
			for (q=n, r=p;*r&&*q;)
			{
				if (!srch_cmp(*q,*r)) r++,q++;    /* same */
				else break;
			}
			if (!*q) return 1;
		}
	}

	return 0;
}

unsigned hash_string(unsigned char *u)
{
	unsigned hash = 0;
	for (; *u; u++)
		hash = hash * 0x11 + *u;
	return hash;
}
