/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2001 Apple Computer, Inc.  All Rights Reserved. The
 * contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License.  Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  Please
 * see the License for the specific language governing rights and
 * limitations under the License.
 *
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */
/*
	File:		StrPtrLen.cpp

	Contains:	Implementation of class defined in StrPtrLen.h.  
					

	

*/


#include <ctype.h>
#include "StrPtrLen.h"
#include "MyAssert.h"
#include "OS.h"
#include "OSMemory.h"


UInt8		StrPtrLen::sCaseInsensitiveMask[] =
{
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, //0-9 
	10, 11, 12, 13, 14, 15, 16, 17, 18, 19, //10-19 
	20, 21, 22, 23, 24, 25, 26, 27, 28, 29, //20-29
	30, 31, 32, 33, 34, 35, 36, 37, 38, 39, //30-39 
	40, 41, 42, 43, 44, 45, 46, 47, 48, 49, //40-49
	50, 51, 52, 53, 54, 55, 56, 57, 58, 59, //50-59
	60, 61, 62, 63, 64, 97, 98, 99, 100, 101, //60-69 //stop on every character except a letter
	102, 103, 104, 105, 106, 107, 108, 109, 110, 111, //70-79
	112, 113, 114, 115, 116, 117, 118, 119, 120, 121, //80-89
	122, 91, 92, 93, 94, 95, 96, 97, 98, 99, //90-99
	100, 101, 102, 103, 104, 105, 106, 107, 108, 109, //100-109
	110, 111, 112, 113, 114, 115, 116, 117, 118, 119, //110-119
	120, 121, 122, 123, 124, 125, 126, 127, 128, 129 //120-129
};

UInt8 StrPtrLen::sNonPrintChars[] =
{
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, //0-9     // stop
    0, 1, 1, 0, 1, 1, 1, 1, 1, 1, //10-19    //'\r' & '\n' are not stop conditions
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //20-29
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, //30-39   
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40-49
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50-59
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //60-69  
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, //170-179
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //180-189
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //190-199
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //200-209
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //210-219
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //220-229
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //230-239
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //240-249
    1, 1, 1, 1, 1, 1             //250-255
};

char* StrPtrLen::GetAsCString() const
{
	// convert to a "NEW'd" zero terminated char array
	// caler is reponsible for the newly allocated memory
	char *theString = NEW char[Len+1];
	
	if ( Ptr && Len > 0 )
		::memcpy( theString, Ptr, Len );
	
	theString[Len] = 0;
	
	return theString;
}


Bool16 StrPtrLen::Equal(const StrPtrLen &compare) const
{
	if (NULL == compare.Ptr && NULL == Ptr )
		return true;
		
        if ((NULL == compare.Ptr) || (NULL == Ptr))
		return false;

	if ((compare.Len == Len) && (memcmp(compare.Ptr, Ptr, Len) == 0))
		return true;
	else
		return false;
}

Bool16 StrPtrLen::Equal(const char* compare) const
{	
	if (NULL == compare && NULL == Ptr )
		return true;
		
	if ((NULL == compare) || (NULL == Ptr))
		return false;
		
	if ((::strlen(compare) == Len) && (memcmp(compare, Ptr, Len) == 0))
		return true;
	else
		return false;
}



Bool16 StrPtrLen::NumEqualIgnoreCase(const char* compare, const UInt32 len) const
{
	// compare thru the first "len: bytes
	Assert(compare != NULL);
	
	if (len <= Len)
	{
		for (UInt32 x = 0; x < len; x++)
			if (sCaseInsensitiveMask[Ptr[x]] != sCaseInsensitiveMask[compare[x]])
				return false;
		return true;
	}
	return false;
}

Bool16 StrPtrLen::EqualIgnoreCase(const char* compare, const UInt32 len) const
{
	Assert(compare != NULL);
	if (len == Len)
	{
		for (UInt32 x = 0; x < len; x++)
			if (sCaseInsensitiveMask[Ptr[x]] != sCaseInsensitiveMask[compare[x]])
				return false;
		return true;
	}
	return false;
}

char *StrPtrLen::FindStringCase(char *queryCharStr, StrPtrLen *resultStr, Bool16 caseSensitive) const
{
	// Be careful about exiting this method from the middle. This routine deletes allocated memory at the end.
	// 

	if (resultStr)
		resultStr->Set(NULL,0);

	Assert (NULL != queryCharStr);
	if (NULL == queryCharStr) return NULL;
	if (NULL == Ptr) return NULL;
	if (0 == Len) return NULL;
	

	StrPtrLen queryStr(queryCharStr);
	char *editSource = NULL;
	char *resultChar = NULL;
	char lastSourceChar = Ptr[Len];
	
	if (lastSourceChar != 0) // need to modify for termination. 
	{	editSource = NEW char[Len + 1]; // Ptr could be a static string so make a copy
		::memcpy( editSource, Ptr, Len );
		editSource[Len] = 0; // this won't work on static strings so we are modifing a new string here
	}

	char *queryString = queryCharStr;		
	char *dupSourceString = NULL;
	char *dupQueryString = NULL;
	char *sourceString = Ptr;
	UInt32 foundLen = 0;
	
	if (editSource != NULL) // a copy of the source ptr and len 0 terminated
		sourceString = editSource;
	
	if (!caseSensitive)
	{	dupSourceString = ::strdup(sourceString);
		dupQueryString = ::strdup(queryCharStr);				
		if (dupSourceString && dupQueryString) 
		{	sourceString = StrPtrLen(dupSourceString).ToUpper();
			queryString = StrPtrLen(dupQueryString).ToUpper();
			resultChar = ::strstr(sourceString,queryString);
			
			::free(dupSourceString);
			::free(dupQueryString);
		}
	}
	else
	{	resultChar = ::strstr(sourceString,queryString);		
	}
	
	if (resultChar != NULL) // get the start offset
	{	foundLen = resultChar - sourceString;
		resultChar = Ptr + foundLen;  // return a pointer in the source buffer
		if (resultChar > (Ptr + Len)) // make sure it is in the buffer
			resultChar = NULL;
	}
	
	if (editSource != NULL)  
		delete [] editSource;
	
	if (resultStr != NULL && resultChar != NULL)
		resultStr->Set(resultChar,queryStr.Len);
	
#if STRPTRLENTESTING	
	printf("StrPtrLen::FindStringCase found string=%s\n",resultChar);
#endif

	return resultChar;
}

void StrPtrLen::PrintStr()
{
    Bool16 ok = true;
    for (UInt32 i = 0; i < Len; i ++) 
    { 
       if (StrPtrLen::sNonPrintChars[Ptr[i]]) 
       {   ok =false;
           break;
       }
    } 
           
    if (ok)
    {   char * thestr = GetAsCString();
        printf("%s",thestr);
        delete thestr;
    }   
}

void StrPtrLen::PrintStrEOL()
{
    Bool16 ok = true;
    for (UInt32 i = 0; i < Len; i ++) 
    { 
       if (StrPtrLen::sNonPrintChars[Ptr[i]]) 
       {   ok =false;
           break;
       }
       
    } 
           
    if (ok)
    {   char * thestr = GetAsCString();
        char * theStrLine = thestr;
        char * nextLine = NULL;
        char * theChar = NULL;
        static char *cr="\\r";
        static char *lf="\\n\n";
        SInt32 tempLen = (SInt32) Len;
        for (SInt32 i = 0; i < tempLen; i ++) 
        {   
            if (theStrLine[i] == '\r')
            {   theChar = cr;
                theStrLine[i] = 0;
                nextLine = &theStrLine[i+1];
            }
            else if (theStrLine[i] == '\n')
            {   theChar = lf;
                theStrLine[i] = 0;
                nextLine = &theStrLine[i+1];
            }
            
            if (nextLine != NULL)
            { 
                printf("%s",theStrLine);
                printf("%s",theChar);
                
                theStrLine = nextLine;
                nextLine = NULL;  
                tempLen -= (i+1);
                i = -1;
            }
        }
        printf("%s",theStrLine);
        delete thestr;
    }   
}

#if STRPTRLENTESTING
Bool16	StrPtrLen::Test()
{
	static char* test1 = "2347.;.][';[;]abcdefghijklmnopqrstuvwxyz#%#$$#";
	static char* test2 = "2347.;.][';[;]ABCDEFGHIJKLMNOPQRSTUVWXYZ#%#$$#";
	static char* test3 = "Content-Type:";
	static char* test4 = "cONTent-TYPe:";
	static char* test5 = "cONTnnt-TYPe:";
	static char* test6 = "cONTent-TY";
	
	static char* test7 = "ontent-Type:";
	static char* test8 = "ONTent-TYPe:";
	static char* test9 = "-TYPe:";
	static char* test10 = ":";
	
	StrPtrLen theVictim1(test1, strlen(test1));
	if (!theVictim1.EqualIgnoreCase(test2, strlen(test2)))
		return false;
		
	if (theVictim1.EqualIgnoreCase(test3, strlen(test3)))
		return false;
	if (!theVictim1.EqualIgnoreCase(test1, strlen(test1)))
		return false;

	StrPtrLen theVictim2(test3, strlen(test3));
	if (!theVictim2.EqualIgnoreCase(test4, strlen(test4)))
		return false;
	if (theVictim2.EqualIgnoreCase(test5, strlen(test5)))
		return false;
	if (theVictim2.EqualIgnoreCase(test6, strlen(test6)))
		return false;
		
	StrPtrLen outResultStr;
	if (!theVictim1.FindStringIgnoreCase(test2, &outResultStr))
		return false;
	if (theVictim1.FindStringIgnoreCase(test3, &outResultStr))
		return false;
	if (!theVictim1.FindStringIgnoreCase(test1, &outResultStr))
		return false;
	if (!theVictim2.FindStringIgnoreCase(test4))
		return false;
	if (theVictim2.FindStringIgnoreCase(test5))
		return false;
	if (!theVictim2.FindStringIgnoreCase(test6))
		return false;
	if (!theVictim2.FindStringIgnoreCase(test7))
		return false;
	if (!theVictim2.FindStringIgnoreCase(test8))
		return false;
	if (!theVictim2.FindStringIgnoreCase(test9))
		return false;
	if (!theVictim2.FindStringIgnoreCase(test10))
		return false;

	if (theVictim1.FindString(test2, &outResultStr))
		return false;
	if (theVictim1.FindString(test3, &outResultStr))
		return false;
	if (!theVictim1.FindString(test1, &outResultStr))
		return false;
	if (theVictim2.FindString(test4))
		return false;
	if (theVictim2.FindString(test5))
		return false;
	if (theVictim2.FindString(test6))
		return false;
	if (!theVictim2.FindString(test7))
		return false;
	if (theVictim2.FindString(test8))
		return false;
	if (theVictim2.FindString(test9))
		return false;
	if (!theVictim2.FindString(test10))
		return false;
	
	StrPtrLen query;
	query.Set(test2);
	if (theVictim1.FindString(query, &outResultStr))
		return false;
	if (outResultStr.Len > 0)
		return false;
	if (outResultStr.Ptr != NULL)
		return false;
		
	query.Set(test3);
	if (theVictim1.FindString(query, &outResultStr))
		return false;
	if (outResultStr.Len > 0)
		return false;
	if (outResultStr.Ptr != NULL)
		return false;
		
	query.Set(test1);
	if (!theVictim1.FindString(query, &outResultStr))
		return false;
	if (!outResultStr.Equal(query))
		return false;
		
	query.Set(test4);
	if (query.Equal(theVictim2.FindString(query)))
		return false;
	
	query.Set(test5);
	if (query.Equal(theVictim2.FindString(query)))
		return false;

	query.Set(test6);
	if (query.Equal(theVictim2.FindString(query)))
		return false;

	query.Set(test7);
	if (!query.Equal(theVictim2.FindString(query)))
		return false;

	query.Set(test8);
	if (query.Equal(theVictim2.FindString(query)))
		return false;

	query.Set(test9);
	if (query.Equal(theVictim2.FindString(query)))
		return false;

	query.Set(test10);
	if (!query.Equal(theVictim2.FindString(query)))
		return false;
	
	query.Set(test10);
	if (!query.Equal(theVictim2.FindString(query)))
		return false;
	
	StrPtrLen partialStaticSource(test1,5);
	query.Set("abcd");
	if (query.Equal(partialStaticSource.FindString(query)))
		return false;
		
	query.Set("47");
	if (query.Equal(partialStaticSource.FindString(query))) // success = !equal because the char str is longer than len
		return false;
	
	if (query.FindString(partialStaticSource.FindString(query))) // success = !found because the 0 term src is not in query
		return false;

	partialStaticSource.FindString(query,&outResultStr);
	if (!outResultStr.Equal(query)) // success =found the result Ptr and Len is the same as the query
		return false;

	return true;
}
#endif
