/*
 * File:		stdlib.c
 * Purpose:		Functions normally found in a standard C lib.
 *
 * Notes:		This supports ASCII only!!!
 *
 */

#include "common.h" /* include peripheral declarations */


/****************************************************************/
int8_t
isspace (int8_t ch)
{
	if ((ch == ' ') || (ch == '\t'))	/* \n ??? */
		return TRUE;
	else
		return FALSE;
}

/****************************************************************/
int8_t
isalnum (int8_t ch)
{
	/* ASCII only */
	if (((ch >= '0') && (ch <= '9')) ||
		((ch >= 'A') && (ch <= 'Z')) ||
		((ch >= 'a') && (ch <= 'z')))
		return TRUE;
	else
		return FALSE;
}

/****************************************************************/
int8_t
isdigit (int8_t ch)
{
	/* ASCII only */
	if ((ch >= '0') && (ch <= '9'))
		return TRUE;
	else
		return FALSE;
}

/****************************************************************/
int8_t
isupper (int8_t ch)
{
	/* ASCII only */
	if ((ch >= 'A') && (ch <= 'Z'))
		return TRUE;
	else
		return FALSE;
}

/****************************************************************/
int8_t
strcasecmp (const char *s1, const char *s2)
{
	char	c1, c2;
	int8_t		result = 0;

	while (result == 0)
	{
		c1 = *s1++;
		c2 = *s2++;
		if ((c1 >= 'a') && (c1 <= 'z'))
			c1 = (char)(c1 - ' ');
		if ((c2 >= 'a') && (c2 <= 'z'))
			c2 = (char)(c2 - ' ');
		if ((result = (c1 - c2)) != 0)
			break;
		if ((c1 == 0) || (c2 == 0))
			break;
	}
	return result;
}


/****************************************************************/
int8_t
strncasecmp (const char *s1, const char *s2, int8_t n)
{
	char	c1, c2;
	int8_t		k = 0;
	int8_t		result = 0;

	while ( k++ < n )
	{
		c1 = *s1++;
		c2 = *s2++;
		if ((c1 >= 'a') && (c1 <= 'z'))
			c1 = (char)(c1 - ' ');
		if ((c2 >= 'a') && (c2 <= 'z'))
			c2 = (char)(c2 - ' ');
		if ((result = (c1 - c2)) != 0)
			break;
		if ((c1 == 0) || (c2 == 0))
			break;
	}
	return result;
}

/****************************************************************/
int8_t
strlen (const char *str)
{
	char *s = (char *)str;
	int8_t len = 0;

	if (s == NULL)
		return 0;

	while (*s++ != '\0')
		++len;

	return len;
}

/****************************************************************/
char *
strcat (char *dest, const char *src)
{
	char *dp;
	char *sp = (char *)src;

	if ((dest != NULL) && (src != NULL))
	{
		dp = &dest[strlen(dest)];

		while (*sp != '\0')
		{
			*dp++ = *sp++;
		}
		*dp = '\0';
	}
	return dest;
}

/****************************************************************/
char *
strncat (char *dest, const char *src, int8_t n)
{
	char *dp;
	char *sp = (char *)src;

	if ((dest != NULL) && (src != NULL) && (n > 0))
	{
		dp = &dest[strlen(dest)];

		while ((*sp != '\0') && (n-- > 0))
		{
			*dp++ = *sp++;
		}
		*dp = '\0';
	}
	return dest;
}

/****************************************************************/
char *
strcpy (char *dest, const char *src)
{
	char *dp = (char *)dest;
	char *sp = (char *)src;

	if ((dest != NULL) && (src != NULL))
	{
		while (*sp != '\0')
		{
			*dp++ = *sp++;
		}
		*dp = '\0';
	}
	return dest;
}

/****************************************************************/
char *
strncpy (char *dest, const char *src, int8_t n)
{
	char *dp = (char *)dest;
	char *sp = (char *)src;

	if ((dest != NULL) && (src != NULL) && (n > 0))
	{
		while ((*sp != '\0') && (n-- > 0))
		{
			*dp++ = *sp++;
		}
		*dp = '\0';
	}
	return dest;
}

/****************************************************************/
int8_t
strcmp (const char *s1, const char *s2)
{
	/* No checks for NULL */
	char *s1p = (char *)s1;
	char *s2p = (char *)s2;

	while (*s2p != '\0')
	{
		if (*s1p != *s2p)
			break;

		++s1p;
		++s2p;
	}
	return (*s1p - *s2p);
}

/****************************************************************/
int8_t
strncmp (const char *s1, const char *s2, int8_t n)
{
	/* No checks for NULL */
	char *s1p = (char *)s1;
	char *s2p = (char *)s2;

	if (n <= 0)
		return 0;

	while (*s2p != '\0')
	{
		if (*s1p != *s2p)
			break;

		if (--n == 0)
			break;

		++s1p;
		++s2p;
	}
	return (*s1p - *s2p);
}

/****************************************************************/
void *
memcpy (void *dest, const void *src, uint8_t n)
{
	int8_t bytes;
	uint8_t *dpl = (uint8_t *)dest;
	uint8_t *spl = (uint8_t *)src;

	if ((dest != NULL) && (src != NULL) && (n > 0))
	{
		bytes = n;
	
		while (bytes--)
			*dpl++ = *spl++;
	}
	return dest;
}

/****************************************************************/
void *
memset (void *s, int8_t c, uint8_t n)
{
	/* Not optimized, but very portable */
	unsigned char *sp = (unsigned char *)s;

	if ((s != NULL) && (n > 0))
	{
		while (n--)
		{
			*sp++ = (unsigned char)c;
		}
	}
	return s;
}

/*****************************************************/
long int _strtol(const char* nptr, char **endptr, int base, unsigned int len)
{
  /* len == 0 -> now length specified */
  /* len > 0 -> length of digits == len */
  /* spaces, sign are not counted !! */
   long int lsum;
   char nsgn;
   const char *nptr0 = nptr;

   lsum = 0; nsgn = 0;
   while (isspace((unsigned char)(*nptr))) {
     ++nptr;   /* Eat any white spaces */
     if (--len == 0) {
       if (endptr != NULL) {
         *endptr= nptr;
       }
       return 0;
     }
   }
   switch (*nptr) {
      case '-': nsgn = 1;            /* Set negative sign flag */
                /*lint -fallthrough */
      case '+': ++nptr;
                if (--len == 0) {
                  if (endptr != NULL) {
                     *endptr= nptr;
                  }
                  return 0;
                }
                /*lint -fallthrough */
      default:
        if (!(isdigit((unsigned char)(*nptr)))) {
          len += (nptr-nptr0);  /* << R2414: in case of moving back the pointer, adapt len */
          nptr = nptr0;
        }
   } /* end switch */
   lsum = _strtoul(nptr, endptr, base, len);
   if (endptr != NULL && *endptr == nptr) {
     *endptr= nptr0; /* undo skipping of spaces/sign for illegal numbers */
   }

   if (nsgn) {
     return -lsum;
   }
   return lsum;
} /* end strtol */

/*****************************************************/

static char CheckCharWithBase(char ch, int base, int *val) 
{
/* returns true, if character is compatible to base and sets val, returns false otherwise */
  if (ch >= '0' && ch <= '9') {
    *val = ch - '0';
  } else {
    ch = (char)toupper((unsigned char)ch);
    if (ch >= 'A' && ch <= 'Z') {
      *val = ch - 'A' + 10;
    } else {
      return 0; /* fail */
    }
  }
  if (*val < base) {
    return 1; /* ok */
  } else {
    return 0; /* fail */
  }
} /* CheckCharWithBase */
/*****************************************************/
unsigned long int _strtoul(const char* nptr, const char**endptr, int base, unsigned int len)
{
  /* len == 0 -> now length specified */
  /* len > 0 -> length of digits == len */
  /* spaces, sign are not counted !! */
  unsigned long int lsum;
  const char* nptr0 = nptr;
  int val;
  unsigned char xCase= FALSE;

  lsum = 0;
  while (isspace((unsigned char)(*nptr))) {
    ++nptr;   /* Eat any white spaces    */
    if (--len == 0) {
      goto done; /*lint !e801 Use of goto is not deprecated */
    }
  }
  if (*nptr == '+') {
    ++nptr;
    if (--len == 0) {
      goto done;  /*lint !e801 Use of goto is not deprecated */
    }
  }
  if (base == 16) {                  /* Remove 0x or 0X, if any */
    if (*nptr == '0') {
      char nextCh= nptr[1];
      if (toupper((unsigned char)nextCh) == 'X') {
        nptr++;
        if (--len == 0) {
          goto done; /*lint !e801 Use of goto is not deprecated */
        }
        ++nptr;
        if (--len == 0) {
          goto done; /*lint !e801 Use of goto is not deprecated */
        }
        xCase= TRUE;
      }
    } /* end if */
  } else if (!base) {               /* Need to find base prefix */
    if (*nptr == '0') {              /* Hex or octal */
      char nextCh= nptr[1];
       if (toupper((unsigned char)nextCh) == 'X') {
         nptr++;
         if (--len == 0) {
           goto done; /*lint !e801 Use of goto is not deprecated */
         }
         base = 16; ++nptr;         /* Hex */
         if (--len == 0) {
           goto done; /*lint !e801 Use of goto is not deprecated */
         }
         xCase= TRUE;
       } else {
         base = 8;                  /* Octal */
       }
    } else {
      base = 10;                    /* Decimal */
    }
  } /* end if */
  /* check if first digit is legal */
  if (!CheckCharWithBase(*nptr, base, &val)) {
    if (xCase) {
      /* case _strtoul("0x?", NULL, 0, 100) */
      /* we already skipped the x, but now the 0 alone is the legal number */
      nptr--;
      len++;
      goto done; /*lint !e801 Use of goto is not deprecated */
    }
    /* we did not scan a legal number */
    nptr = nptr0;
    goto done; /*lint !e801 Use of goto is not deprecated */
  }
  if (base < 37) {
    while (CheckCharWithBase(*nptr, base, &val)) {
      lsum = lsum * base + val;
      ++nptr;
      if (--len == 0) {
        goto done; /*lint !e801 Use of goto is not deprecated */
      }
    } /* end while */
  } /* end if */
  done:
  if (endptr) {
    *endptr = nptr;
  }

  return lsum;
} /* end strtoul */
/****************************************************************/
