/*
 * Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
 * reserved.
 * Copyright 2018-present Facebook, Inc.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * string.c: hastily cobbled-together string functions
 */

#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <lame_string.h>

static int char_to_val(char c)
{
	if (c >= '0' && c <= '9')	/* digits */
		return c - '0';
	if (c >= 'A' && c <= 'F')	/* uppercase */
		return c - 'A' + 10;
	if (c >= 'a' && c <= 'f')	/* lowercase */
		return c - 'a' + 10;
	return -1;
}

unsigned long long int strtoull(const char *nptr, char **endptr, int base)
{
	unsigned long long int val;
	size_t i, error = 0;

	/* TODO: enforce lameness of this API for now... */
	assert((base == 0) || (base == 16) || base == 10);

	if (!nptr)
		return 0;

	/* Trim whitespace */
	for (i = 0; i < strlen(nptr); i++)
		if (nptr[i] != ' ')
			break;

	if (base == 0) {
		/* Autodetect base */
		if (strlen(&nptr[i]) >= 2 && ((nptr[i] == '0') &&
		    ((nptr[i + 1] == 'x') || (nptr[i + 1] == 'X')))) {
			base = 16;
			i += 2;	/* start loop after prefix */
		} else
			base = 10;
	}

	val = 0;
	for (; i < strlen(nptr); i++) {
		if (base == 16) {
			if (!isxdigit(nptr[i])) {
				if (*endptr)
					*endptr = (char *)&nptr[i];
				error = 1;
				break;
			}
		} else {
			if (!isdigit(nptr[i])) {
				if (*endptr)
					*endptr = (char *)&nptr[i];
				error = 1;
				break;
			}
		}

		val *= base;
		val += char_to_val(nptr[i]);
	}

	if (error) {
		printk(BIOS_ERR, "Failed to convert string '%s', base %d to "
		       "int\n", nptr, base);
		return 0;
	}
	return val;
}

unsigned long int strtoul(const char *nptr, char **endptr, int base)
{
	unsigned long long int u = strtol(nptr, endptr, base);
	/* FIXME: check for overflow (u > max) */
	return (unsigned long int)u;
}

long int strtol(const char *nptr, char **endptr, int base)
{
	unsigned long long int u;
	int is_neg = 0;
	const char *p;
	long int ret;

	if (nptr[0] == '-') {
		is_neg = 1;
		p = &nptr[1];
	} else {
		p = &nptr[0];
	}
	u = strtoull(p, NULL, base);
	/* FIXME: check for overflow (u > max) */
	if (is_neg)
		ret = 0 - (long int)u;
	else
		ret = (long int)u;
	return ret;
}

long long int strtoll(const char *nptr, char **endptr, int base)
{
	unsigned long long int u;
	int is_neg = 0;
	const char *p;
	long long int ret;

	if (nptr[0] == '-') {
		is_neg = 1;
		p = &nptr[1];
	} else {
		p = &nptr[0];
	}
	u = strtoull(p, NULL, base);
	/* FIXME: check for overflow (sign-bit set) */
	if (is_neg)
		ret = 0 - (long long int)u;
	else
		ret = (long long int)u;
	return ret;
}

/* FIXME: replace sscanf() usage for bdk_config_get_int. returns number of
 * strings converted, so 1 if successful and 0 if not */
int str_to_int(const char *str, int64_t *val)
{
	*val = strtol(str, NULL, 10);
	return 1;
}

/* FIXME: replace sscanf() usage for bdk_config_get_int. returns number of
 * strings converted, so 1 if successful and 0 if not */
int str_to_hex(const char *str, int64_t *val)
{
	*val = strtol(str, NULL, 16);
	return 1;
}