/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */


#include "xp.h"
#include "xp_str.h"
#include <ctype.h>
#include <stdarg.h>

#ifdef PROFILE
#pragma profile on
#endif

#if !defined(XP_WIN) && !defined(XP_OS2) /* keep this out of the winfe */
#if defined(DEBUG)

char *
NOT_NULL (const char * p)
{
	XP_ASSERT(p);
	return (char*) p;
}

#endif
#endif

#define TOLOWER_TABLE_SIZE 128

PRIVATE uint8 *
xp_get_tolower_table()
{
	static uint8 tolower_table[TOLOWER_TABLE_SIZE];
    static XP_Bool first_time = TRUE;

    if(first_time)
    {
        int i;
        first_time = FALSE;

        /* build the tolower_table */
        for(i=0; i < sizeof(tolower_table); i++)
        {
            tolower_table[i] = (uint8) tolower((char)i);
        }
    }

	return tolower_table;
}

PRIVATE uint8 *
xp_get_toupper_table()
{
	static uint8 toupper_table[TOLOWER_TABLE_SIZE];
    static XP_Bool first_time = TRUE;

    if(first_time)
    {
        int i;
        first_time = FALSE;

        /* build the tolower_table */
        for(i=0; i < sizeof(toupper_table); i++)
        {
            toupper_table[i] = (uint8) toupper((char)i);
        }
    }

	return toupper_table;
}

#define XP_STR_TOUPPER(c) (c < TOLOWER_TABLE_SIZE ? toupper_table[c] : c)
#define XP_STR_TOLOWER(c) (c < TOLOWER_TABLE_SIZE ? tolower_table[c] : c)

/* fast table driven tolower routine 
 *
 * We only deal with 7 bit ascii
 */
PUBLIC int
xp_tolower(int c)
{
	uint8 *tolower_table = xp_get_tolower_table();

	return XP_STR_TOLOWER(c);
}

/* fast table driven toupper routine 
 *
 * We only deal with 7 bit ascii
 */
PUBLIC int
xp_toupper(int c)
{
	uint8 *toupper_table = xp_get_toupper_table();

	return XP_STR_TOUPPER(c);
}

/* find a substring within a string with a case insensitive search
 */
PUBLIC char *
strcasestr (const char * str, const char * substr)
{
    register const char *pA;
    register const char *pB;
    register const char *pC;
	uint8 *toupper_table = xp_get_toupper_table();

	if(!str)
		return(NULL);
	
	for(pA=str; *pA; pA++)
	  {
		if(XP_STR_TOUPPER(*pA) == XP_STR_TOUPPER(*substr))
		  {
    		for(pB=pA, pC=substr; ; pB++, pC++)
              {
                if(!(*pC))
                    return((char *)pA);

				if(!(*pB))
					break;

				if(XP_STR_TOUPPER(*pB) != XP_STR_TOUPPER(*pC))
					break;
              }
		  }
	  }

	return(NULL);
}

/* find a char within a specified length string
 */
PUBLIC char *
strnchr (const char * str, const char single, int32 len)
{
	register int count=0;
    register const char *pA;

    if(!str)
        return(NULL);
   
    for(pA=str; count < len; pA++, count++)
      {
        if(*pA == single)
          {
             return((char *)pA);
          }

		if(!(*pA))
			return(NULL);
      }

    return(NULL);
}

/* find a substring within a specified length string with a case 
 * insensitive search
 */
PUBLIC char *
strncasestr (const char * str, const char * substr, int32 len)
{
	register int count=0;
	register int count2=0;
    register const char *pA;
    register const char *pB;
    register const char *pC;
	uint8 *toupper_table = xp_get_toupper_table();

    if(!str || !substr)
        return(NULL);
   
    for(pA=str; count < len; pA++, count++)
      {
        if(XP_STR_TOUPPER(*pA) == XP_STR_TOUPPER(*substr))
          {
            for(pB=pA, pC=substr, count2=count; count2<len; 
													count2++,pB++,pC++)
              {
                if(!(*pC))
                    return((char *)pA);

                if(!(*pB))
                    break;

                if(XP_TO_UPPER(*pB) != XP_TO_UPPER(*pC))
                    break;
              }
          }
      }

    return(NULL);
}


/*	compare strings in a case insensitive manner
*/
PUBLIC int 
strcasecomp (const char* one, const char *two)
{
	const char *pA;
	const char *pB;
	uint8 *toupper_table = xp_get_toupper_table();

	for(pA=one, pB=two; *pA && *pB; pA++, pB++) 
	  {
	    register int tmp = XP_STR_TOUPPER(*pA) - XP_STR_TOUPPER(*pB);
	    if (tmp) 
			return tmp;
	  }

	if (*pA) 
		return 1;	
	if (*pB) 
		return -1;
	return 0;	
}


/*	compare strings in a case insensitive manner with a length limit
*/
PUBLIC int 
strncasecomp (const char* one, const char * two, int n)
{
	const char *pA;
	const char *pB;
	uint8 *toupper_table = xp_get_toupper_table();
	
	for(pA=one, pB=two;; pA++, pB++) 
	  {
	    int tmp;
	    if (pA == one+n) 
			return 0;	
	    if (!(*pA && *pB)) 
			return *pA - *pB;
	    tmp = XP_STR_TOUPPER(*pA) - XP_STR_TOUPPER(*pB);
	    if (tmp) 
			return tmp;
	  }
}

/*	Allocate a new copy of a block of binary data, and returns it
 */
PUBLIC char * 
NET_BACopy (char **destination, const char *source, size_t length)
{
	if(*destination)
	  {
	    XP_FREE(*destination);
		*destination = 0;
	  }

    if (! source)
	  {
        *destination = NULL;
	  }
    else 
	  {
        *destination = (char *) XP_ALLOC (length);
        if (*destination == NULL) 
	        return(NULL);
        XP_MEMCPY(*destination, source, length);
      }
    return *destination;
}

/*	binary block Allocate and Concatenate
 *
 *   destination_length  is the length of the existing block
 *   source_length   is the length of the block being added to the 
 *   destination block
 */
PUBLIC char * 
NET_BACat (char **destination, 
		   size_t destination_length, 
		   const char *source, 
		   size_t source_length)
{
    if (source) 
	  {
        if (*destination) 
	      {
      	    *destination = (char *) XP_REALLOC (*destination, destination_length + source_length);
            if (*destination == NULL) 
	          return(NULL);

            XP_MEMMOVE (*destination + destination_length, source, source_length);

          } 
		else 
		  {
            *destination = (char *) XP_ALLOC (source_length);
            if (*destination == NULL) 
	          return(NULL);

            XP_MEMCPY(*destination, source, source_length);
          }
    }

  return *destination;
}

/*	Very similar to strdup except it free's too
 */
PUBLIC char * 
NET_SACopy (char **destination, const char *source)
{
	if(*destination)
	  {
	    XP_FREE(*destination);
		*destination = 0;
	  }
    if (! source)
	  {
        *destination = NULL;
	  }
    else 
	  {
        *destination = (char *) XP_ALLOC (XP_STRLEN(source) + 1);
        if (*destination == NULL) 
 	        return(NULL);

        XP_STRCPY (*destination, source);
      }
    return *destination;
}

/*  Again like strdup but it concatinates and free's and uses Realloc
*/
PUBLIC char *
NET_SACat (char **destination, const char *source)
{
    if (source && *source)
      {
        if (*destination)
          {
            int length = XP_STRLEN (*destination);
            *destination = (char *) XP_REALLOC (*destination, length + XP_STRLEN(source) + 1);
            if (*destination == NULL)
            return(NULL);

            XP_STRCPY (*destination + length, source);
          }
        else
          {
            *destination = (char *) XP_ALLOC (XP_STRLEN(source) + 1);
            if (*destination == NULL)
                return(NULL);

             XP_STRCPY (*destination, source);
          }
      }
    return *destination;
}

/* remove front and back white space
 * modifies the original string
 */
PUBLIC char *
XP_StripLine (char *string)
{
    char * ptr;

	/* remove leading blanks */
    while(*string=='\t' || *string==' ' || *string=='\r' || *string=='\n')
		string++;    

    for(ptr=string; *ptr; ptr++)
		;   /* NULL BODY; Find end of string */

	/* remove trailing blanks */
    for(ptr--; ptr >= string; ptr--) 
	  {
        if(*ptr=='\t' || *ptr==' ' || *ptr=='\r' || *ptr=='\n') 
			*ptr = '\0'; 
        else 
			break;
	  }

    return string;
}

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

char *XP_AppendStr(char *in, const char *append)
{
    int alen, inlen;

    alen = XP_STRLEN(append);
    if (in) {
		inlen = XP_STRLEN(in);
		in = (char*) XP_REALLOC(in,inlen+alen+1);
		if (in) {
			XP_MEMCPY(in+inlen, append, alen+1);
		}
    } else {
		in = (char*) XP_ALLOC(alen+1);
		if (in) {
			XP_MEMCPY(in, append, alen+1);
		}
    }
    return in;
}

char *XP_Cat(char *a0, ...)
{
    va_list ap;
    char *a, *result, *cp;
    int len;

	/* Count up string length's */
    va_start(ap, a0);
	len = 1;
	a = a0;
    while (a != (char*) NULL) {
		len += XP_STRLEN(a);
		a = va_arg(ap, char*);
    }
	va_end(ap);

	/* Allocate memory and copy strings */
    va_start(ap, a0);
	result = cp = (char*) XP_ALLOC(len);
	if (!cp) return 0;
	a = a0;
	while (a != (char*) NULL) {
		len = XP_STRLEN(a);
		XP_MEMCPY(cp, a, len);
		cp += len;
		a = va_arg(ap, char*);
	}
	*cp = 0;
	va_end(ap);
    return result;
}

/************************************************************************
 * These are "safe" versions of the runtime library routines. The RTL
 * versions do not null-terminate dest IFF strlen(src) >= destLength.
 * These versions always null-terminate, which is why they're safe.
 */

char *XP_STRNCAT_SAFE (char *dest, const char *src, size_t maxToCat)
{
	int destLen; 
	char *result; 

	destLen = XP_STRLEN (dest);
	result = strncat (dest, src, --maxToCat);
	dest[destLen + maxToCat] = '\0';
	return result;
}

char *XP_STRNCPY_SAFE (char *dest, const char *src, size_t destLength)
{
	char *result = strncpy (dest, src, --destLength);
	dest[destLength] = '\0';
	return result;
}

/*************************************************
   The following functions are used to implement
   a thread safe strtok
 *************************************************/
/*
 * Get next token from string *stringp, where tokens are (possibly empty)
 * strings separated by characters from delim.  Tokens are separated
 * by exactly one delimiter iff the skip parameter is false; otherwise
 * they are separated by runs of characters from delim, because we
 * skip over any initial `delim' characters.
 *
 * Writes NULs into the string at *stringp to end tokens.
 * delim will usually, but need not, remain CONSTant from call to call.
 * On return, *stringp points past the last NUL written (if there might
 * be further tokens), or is NULL (if there are definitely no more tokens).
 *
 * If *stringp is NULL, strtoken returns NULL.
 */
static 
char *strtoken_r(char ** stringp, const char *delim, int skip)
{
	char *s;
	const char *spanp;
	int c, sc;
	char *tok;

	if ((s = *stringp) == NULL)
		return (NULL);

	if (skip) {
		/*
		 * Skip (span) leading delimiters (s += strspn(s, delim)).
		 */
	cont:
		c = *s;
		for (spanp = delim; (sc = *spanp++) != 0;) {
			if (c == sc) {
				s++;
				goto cont;
			}
		}
		if (c == 0) {		/* no token found */
			*stringp = NULL;
			return (NULL);
		}
	}

	/*
	 * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
	 * Note that delim must have one NUL; we stop if we see that, too.
	 */
	for (tok = s;;) {
		c = *s++;
		spanp = delim;
		do {
			if ((sc = *spanp++) == c) {
				if (c == 0)
					s = NULL;
				else
					s[-1] = 0;
				*stringp = s;
				return( (char *) tok );
			}
		} while (sc != 0);
	}
	/* NOTREACHED */
}


char *XP_STRTOK_R(char *s1, const char *s2, char **lasts)
{
	if (s1)
		*lasts = s1;
	return (strtoken_r(lasts, s2, 1));
}


#ifdef PROFILE
#pragma profile off
#endif
