/*************************************************************************
 *
 * $Id: strio.c,v 1.1 2001/06/07 08:23:02 fjfranklin Exp $
 *
 * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
 *
 ************************************************************************/

/*
 * TODO
 * - StrToLongDouble
 */
 
#if defined(unix) || defined(__xlC__)
# define PLATFORM_UNIX
#elif defined(WIN32) || defined(_WIN32)
# define PLATFORM_WIN32
#elif defined(AMIGA) && defined(__GNUC__)
# define PLATFORM_UNIX
#endif

#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)
# define TRIO_C99
#endif

#include "strio.h"
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#include <math.h>
#ifndef DEBUG
# define NDEBUG
#endif
#include <assert.h>

#ifndef NULL
# define NULL 0
#endif
#define NIL ((char)0)
#ifndef FALSE
# define FALSE (1 == 0)
# define TRUE (! FALSE)
#endif

#define VALID(x) (NULL != (x))
#define INVALID(x) (NULL == (x))

#if defined(PLATFORM_UNIX)
# define USE_STRCASECMP
# define USE_STRNCASECMP
# define USE_STRERROR
#elif defined(PLATFORM_WIN32)
# define USE_STRCMPI
#endif

/*************************************************************************
 * StrAppendMax
 */
char *StrAppendMax(char *target, size_t max, const char *source)
{
  assert(VALID(target));
  assert(VALID(source));
  assert(max > 0);

  max -= StrLength(target) + 1;
  return (max > 0) ? strncat(target, source, max) : target;
}

/*************************************************************************
 * StrCopyMax
 */
char *StrCopyMax(char *target, size_t max, const char *source)
{
  assert(VALID(target));
  assert(VALID(source));
  assert(max > 0); /* Includes != 0 */

  target = strncpy(target, source, max - 1);
  target[max - 1] = (char)0;
  return target;
}

/*************************************************************************
 * StrDuplicate
 */
char *StrDuplicate(const char *source)
{
  char *target;

  assert(VALID(source));

  target = (char *)malloc(StrLength(source) + 1);
  if (target)
    {
      StrCopy(target, source);
    }
  return target;
}

/*************************************************************************
 * StrDuplicateMax
 */
char *StrDuplicateMax(const char *source, size_t max)
{
  char *target;
  size_t len;

  assert(VALID(source));
  assert(max > 0);

  /* Make room for string plus a terminating zero */
  len = StrLength(source) + 1;
  if (len > max)
    {
      len = max;
    }
  target = (char *)malloc(len);
  if (target)
    {
      StrCopyMax(target, len, source);
    }
  return target;
}

/*************************************************************************
 * StrEqual
 */
int StrEqual(const char *first, const char *second)
{
  assert(VALID(first));
  assert(VALID(second));

  if (VALID(first) && VALID(second))
    {
#if defined(USE_STRCASECMP)
      return (0 == strcasecmp(first, second));
#elif defined(USE_STRCMPI)
      return (0 == strcmpi(first, second));
#else
      while ((*first != NIL) && (*second != NIL))
	{
	  if (toupper(*first) != toupper(*second))
	    {
	      break;
	    }
	  first++;
	  second++;
	}
      return ((*first == NIL) && (*second == NIL));
#endif
    }
  return FALSE;
}

/*************************************************************************
 * StrEqualCase
 */
int StrEqualCase(const char *first, const char *second)
{
  assert(VALID(first));
  assert(VALID(second));

  if (VALID(first) && VALID(second))
    {
      return (0 == strcmp(first, second));
    }
  return FALSE;
}

/*************************************************************************
 * StrEqualCaseMax
 */
int StrEqualCaseMax(const char *first, size_t max, const char *second)
{
  assert(VALID(first));
  assert(VALID(second));

  if (VALID(first) && VALID(second))
    {
      return (0 == strncmp(first, second, max));
    }
  return FALSE;
}

/*************************************************************************
 * StrEqualMax
 */
int StrEqualMax(const char *first, size_t max, const char *second)
{
  assert(VALID(first));
  assert(VALID(second));

  if (VALID(first) && VALID(second))
    {
#if defined(USE_STRNCASECMP)
      return (0 == strncasecmp(first, second, max));
#else
      /* Not adequately tested yet */
      size_t cnt = 0;
      while ((*first != NIL) && (*second != NIL) && (cnt <= max))
	{
	  if (toupper(*first) != toupper(*second))
	    {
	      break;
	    }
	  first++;
	  second++;
	  cnt++;
	}
      return ((cnt == max) || ((*first == NIL) && (*second == NIL)));
#endif
    }
  return FALSE;
}

/*************************************************************************
 * StrError
 */
const char *StrError(int errorNumber)
{
#if defined(USE_STRERROR)
  return strerror(errorNumber);
#else
  return "unknown";
#endif
}

/*************************************************************************
 * StrFormatDate
 */
size_t StrFormatDateMax(char *target,
			size_t max,
			const char *format,
			const struct tm *datetime)
{
  assert(VALID(target));
  assert(VALID(format));
  assert(VALID(datetime));
  assert(max > 0);
  
  return strftime(target, max, format, datetime);
}

/*************************************************************************
 * StrHash
 */
unsigned long StrHash(const char *string, int type)
{
  unsigned long value = 0L;
  char ch;

  assert(VALID(string));
  
  switch (type)
    {
    case STRIO_HASH_PLAIN:
      while ( (ch = *string++) != NIL )
	{
	  value *= 31;
	  value += (unsigned long)ch;
	}
      break;
    default:
      assert(FALSE);
      break;
    }
  return value;
}

/*************************************************************************
 * StrMatch
 */
int StrMatch(char *string, char *pattern)
{
  assert(VALID(string));
  assert(VALID(pattern));
  
  for (; ('*' != *pattern); ++pattern, ++string)
    {
      if (NIL == *string)
	{
	  return (NIL == *pattern);
	}
      if ((toupper(*string) != toupper(*pattern))
	  && ('?' != *pattern))
	{
	  return FALSE;
	}
    }
  /* two-line patch to prevent *too* much recursiveness: */
  while ('*' == pattern[1])
    pattern++;

  do
    {
      if ( StrMatch(string, &pattern[1]) )
	{
	  return TRUE;
	}
    }
  while (*string++);
  
  return FALSE;
}

/*************************************************************************
 * StrMatchCase
 */
int StrMatchCase(char *string, char *pattern)
{
  assert(VALID(string));
  assert(VALID(pattern));
  
  for (; ('*' != *pattern); ++pattern, ++string)
    {
      if (NIL == *string)
	{
	  return (NIL == *pattern);
	}
      if ((*string != *pattern)
	  && ('?' != *pattern))
	{
	  return FALSE;
	}
    }
  /* two-line patch to prevent *too* much recursiveness: */
  while ('*' == pattern[1])
    pattern++;

  do
    {
      if ( StrMatchCase(string, &pattern[1]) )
	{
	  return TRUE;
	}
    }
  while (*string++);
  
  return FALSE;
}

/*************************************************************************
 * StrSpanFunction
 *
 * Untested
 */
size_t StrSpanFunction(char *source, int (*Function)(int))
{
  size_t count = 0;

  assert(VALID(source));
  assert(VALID(Function));
  
  while (*source != NIL)
    {
      if (Function(*source))
	break; /* while */
      source++;
      count++;
    }
  return count;
}

/*************************************************************************
 * StrSubstringMax
 */
char *StrSubstringMax(const char *string, size_t max, const char *find)
{
  size_t count;
  size_t size;
  char *result = NULL;

  assert(VALID(string));
  assert(VALID(find));
  
  size = StrLength(find);
  if (size <= max)
    {
      for (count = 0; count <= max - size; count++)
	{
	  if (StrEqualMax(find, size, &string[count]))
	    {
	      result = (char *)&string[count];
	      break;
	    }
	}
    }
  return result;
}

/*************************************************************************
 * StrToDouble
 *
 * double ::= [ <sign> ]
 *            ( <number> |
 *              <number> <decimal_point> <number> |
 *              <decimal_point> <number> )
 *            [ <exponential> [ <sign> ] <number> ]
 * number ::= 1*( <digit> )
 * digit ::= ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
 * exponential ::= ( 'e' | 'E' )
 * sign ::= ( '-' | '+' )
 * decimal_point ::= '.'
 */
double StrToDouble(const char *source, char **endp)
{
#if defined(TRIO_C99)
  return strtod(source, endp);
#else
  /* Preliminary code */
  int isNegative = FALSE;
  int isExponentNegative = FALSE;
  unsigned long integer = 0;
  unsigned long fraction = 0;
  unsigned long fracdiv = 1;
  unsigned long exponent = 0;
  double value = 0.0;

  /* First try hex-floats */
  if ((source[0] == '0') && ((source[1] == 'x') || (source[1] == 'X')))
    {
      source += 2;
      while (isxdigit((int)*source))
	{
	  integer *= 16;
	  integer += (isdigit((int)*source) ? (*source - '0') :
	    (isupper((int)*source) ? (*source - 'A') :
	     (*source - 'a')));
	  source++;
	}
      if (*source == '.')
	{
	  source++;
	  while (isxdigit((int)*source))
	    {
	      fraction *= 16;
	      fraction += (isdigit((int)*source) ? (*source - '0') :
			   (isupper((int)*source) ? (*source - 'A') :
			    (*source - 'a')));
	      fracdiv *= 16;
	      source++;
	    }
	  if ((*source == 'p') || (*source == 'P'))
	    {
	      source++;
	      if ((*source == '+') || (*source == '-'))
		{
		  isExponentNegative = (*source == '-');
		  source++;
		}
	      while (isdigit((int)*source))
		{
		  exponent *= 10;
		  exponent += (*source - '0');
		  source++;
		}
	    }
	}
    }
  else /* Then try normal decimal floats */
    {
      isNegative = (*source == '-');
      /* Skip sign */
      if ((*source == '+') || (*source == '-'))
	source++;

      /* Integer part */
      while (isdigit((int)*source))
	{
	  integer *= 10;
	  integer += (*source - '0');
	  source++;
	}

      if (*source == '.')
	{
	  source++; /* skip decimal point */
	  while (isdigit((int)*source))
	    {
	      fraction *= 10;
	      fraction += (*source - '0');
	      fracdiv *= 10;
	      source++;
	    }
	}
      if ((*source == 'e') || (*source == 'E'))
	{
	  source++; /* Skip exponential indicator */
	  isExponentNegative = (*source == '-');
	  if ((*source == '+') || (*source == '-'))
	    source++;
	  while (isdigit((int)*source))
	    {
	      exponent *= 10;
	      exponent += (*source - '0');
	      source++;
	    }
	}
    }
  
  value = (double)integer;
  if (fraction != 0)
    {
      value += (double)fraction / (double)fracdiv;
    }
  if (exponent != 0)
    {
      if (isExponentNegative)
	value /= pow((double)10, (double)exponent);
      else
	value *= pow((double)10, (double)exponent);
    }
  if (isNegative)
    value = -value;

  if (endp)
    *endp = source;
  return value;
#endif
}

/*************************************************************************
 * StrToFloat
 */
float StrToFloat(const char *source, char **endp)
{
#if defined(TRIO_C99)
  return strtof(source, endp);
#else
  return (float)StrToDouble(source, endp);
#endif
}

/*************************************************************************
 * StrToUpper
 */
int StrToUpper(char *target)
{
  int i = 0;

  assert(VALID(target));
  
  while (NIL != *target)
    {
      *target = toupper(*target);
      target++;
      i++;
    }
  return i;
}
