| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| /* |
| * pkix_pl_string.c |
| * |
| * String Object Functions |
| * |
| */ |
| |
| #include "pkix_pl_string.h" |
| |
| /* --Private-String-Functions------------------------------------- */ |
| |
| /* |
| * FUNCTION: pkix_pl_String_Comparator |
| * (see comments for PKIX_PL_ComparatorCallback in pkix_pl_system.h) |
| * |
| * NOTE: |
| * This function is a utility function called by pkix_pl_String_Equals(). |
| * It is not officially registered as a comparator. |
| */ |
| static PKIX_Error * |
| pkix_pl_String_Comparator( |
| PKIX_PL_String *firstString, |
| PKIX_PL_String *secondString, |
| PKIX_Int32 *pResult, |
| void *plContext) |
| { |
| PKIX_UInt32 i; |
| PKIX_Int32 result; |
| unsigned char *p1 = NULL; |
| unsigned char *p2 = NULL; |
| |
| PKIX_ENTER(STRING, "pkix_pl_String_Comparator"); |
| PKIX_NULLCHECK_THREE(firstString, secondString, pResult); |
| |
| result = 0; |
| |
| p1 = (unsigned char*) firstString->utf16String; |
| p2 = (unsigned char*) secondString->utf16String; |
| |
| /* Compare characters until you find a difference */ |
| for (i = 0; ((i < firstString->utf16Length) && |
| (i < secondString->utf16Length) && |
| result == 0); i++, p1++, p2++) { |
| if (*p1 < *p2){ |
| result = -1; |
| } else if (*p1 > *p2){ |
| result = 1; |
| } |
| } |
| |
| /* If two arrays are identical so far, the longer one is greater */ |
| if (result == 0) { |
| if (firstString->utf16Length < secondString->utf16Length) { |
| result = -1; |
| } else if (firstString->utf16Length > |
| secondString->utf16Length) { |
| result = 1; |
| } |
| } |
| |
| *pResult = result; |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_String_Destroy |
| * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_pl_String_Destroy( |
| PKIX_PL_Object *object, |
| void *plContext) |
| { |
| PKIX_PL_String *string = NULL; |
| |
| PKIX_ENTER(STRING, "pkix_pl_String_Destroy"); |
| PKIX_NULLCHECK_ONE(object); |
| |
| PKIX_CHECK(pkix_CheckType(object, PKIX_STRING_TYPE, plContext), |
| PKIX_ARGUMENTNOTSTRING); |
| |
| string = (PKIX_PL_String*)object; |
| |
| /* XXX For debugging Destroy EscASCII String */ |
| if (string->escAsciiString != NULL) { |
| PKIX_FREE(string->escAsciiString); |
| string->escAsciiString = NULL; |
| string->escAsciiLength = 0; |
| } |
| |
| /* Destroy UTF16 String */ |
| if (string->utf16String != NULL) { |
| PKIX_FREE(string->utf16String); |
| string->utf16String = NULL; |
| string->utf16Length = 0; |
| } |
| |
| cleanup: |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_String_ToString |
| * (see comments for PKIX_PL_ToStringCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_pl_String_ToString( |
| PKIX_PL_Object *object, |
| PKIX_PL_String **pString, |
| void *plContext) |
| { |
| PKIX_PL_String *string = NULL; |
| char *ascii = NULL; |
| PKIX_UInt32 length; |
| |
| PKIX_ENTER(STRING, "pkix_pl_String_ToString"); |
| PKIX_NULLCHECK_TWO(object, pString); |
| |
| PKIX_CHECK(pkix_CheckType(object, PKIX_STRING_TYPE, plContext), |
| PKIX_ARGUMENTNOTSTRING); |
| |
| string = (PKIX_PL_String*)object; |
| |
| PKIX_CHECK(PKIX_PL_String_GetEncoded |
| (string, PKIX_ESCASCII, (void **)&ascii, &length, plContext), |
| PKIX_STRINGGETENCODEDFAILED); |
| |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, ascii, 0, pString, plContext), |
| PKIX_STRINGCREATEFAILED); |
| |
| goto cleanup; |
| |
| cleanup: |
| |
| PKIX_FREE(ascii); |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_String_Equals |
| * (see comments for PKIX_PL_EqualsCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_pl_String_Equals( |
| PKIX_PL_Object *firstObject, |
| PKIX_PL_Object *secondObject, |
| PKIX_Boolean *pResult, |
| void *plContext) |
| { |
| PKIX_UInt32 secondType; |
| PKIX_Int32 cmpResult = 0; |
| |
| PKIX_ENTER(STRING, "pkix_pl_String_Equals"); |
| PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult); |
| |
| /* Sanity check: Test that "firstObject" is a Strings */ |
| PKIX_CHECK(pkix_CheckType(firstObject, PKIX_STRING_TYPE, plContext), |
| PKIX_FIRSTOBJECTNOTSTRING); |
| |
| /* "SecondObject" doesn't have to be a string */ |
| PKIX_CHECK(PKIX_PL_Object_GetType |
| (secondObject, &secondType, plContext), |
| PKIX_COULDNOTGETTYPEOFSECONDARGUMENT); |
| |
| /* If types differ, then we will return false */ |
| *pResult = PKIX_FALSE; |
| |
| if (secondType != PKIX_STRING_TYPE) goto cleanup; |
| |
| /* It's safe to cast here */ |
| PKIX_CHECK(pkix_pl_String_Comparator |
| ((PKIX_PL_String*)firstObject, |
| (PKIX_PL_String*)secondObject, |
| &cmpResult, |
| plContext), |
| PKIX_STRINGCOMPARATORFAILED); |
| |
| /* Strings are equal iff Comparator Result is 0 */ |
| *pResult = (cmpResult == 0); |
| |
| cleanup: |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_String_Hashcode |
| * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_pl_String_Hashcode( |
| PKIX_PL_Object *object, |
| PKIX_UInt32 *pHashcode, |
| void *plContext) |
| { |
| PKIX_PL_String *string = NULL; |
| |
| PKIX_ENTER(STRING, "pkix_pl_String_Hashcode"); |
| PKIX_NULLCHECK_TWO(object, pHashcode); |
| |
| PKIX_CHECK(pkix_CheckType(object, PKIX_STRING_TYPE, plContext), |
| PKIX_OBJECTNOTSTRING); |
| |
| string = (PKIX_PL_String*)object; |
| |
| PKIX_CHECK(pkix_hash |
| ((const unsigned char *)string->utf16String, |
| string->utf16Length, |
| pHashcode, |
| plContext), |
| PKIX_HASHFAILED); |
| |
| cleanup: |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_String_RegisterSelf |
| * DESCRIPTION: |
| * Registers PKIX_STRING_TYPE and its related functions with systemClasses[] |
| * THREAD SAFETY: |
| * Not Thread Safe - for performance and complexity reasons |
| * |
| * Since this function is only called by PKIX_PL_Initialize, which should |
| * only be called once, it is acceptable that this function is not |
| * thread-safe. |
| */ |
| PKIX_Error * |
| pkix_pl_String_RegisterSelf( |
| void *plContext) |
| { |
| extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; |
| pkix_ClassTable_Entry entry; |
| |
| PKIX_ENTER(STRING, "pkix_pl_String_RegisterSelf"); |
| |
| entry.description = "String"; |
| entry.objCounter = 0; |
| entry.typeObjectSize = sizeof(PKIX_PL_String); |
| entry.destructor = pkix_pl_String_Destroy; |
| entry.equalsFunction = pkix_pl_String_Equals; |
| entry.hashcodeFunction = pkix_pl_String_Hashcode; |
| entry.toStringFunction = pkix_pl_String_ToString; |
| entry.comparator = NULL; |
| entry.duplicateFunction = pkix_duplicateImmutable; |
| |
| systemClasses[PKIX_STRING_TYPE] = entry; |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| |
| /* --Public-String-Functions----------------------------------------- */ |
| |
| /* |
| * FUNCTION: PKIX_PL_String_Create (see comments in pkix_pl_system.h) |
| */ |
| PKIX_Error * |
| PKIX_PL_String_Create( |
| PKIX_UInt32 fmtIndicator, |
| const void *stringRep, |
| PKIX_UInt32 stringLen, |
| PKIX_PL_String **pString, |
| void *plContext) |
| { |
| PKIX_PL_String *string = NULL; |
| unsigned char *utf16Char = NULL; |
| PKIX_UInt32 i; |
| |
| PKIX_ENTER(STRING, "PKIX_PL_String_Create"); |
| PKIX_NULLCHECK_TWO(pString, stringRep); |
| |
| PKIX_CHECK(PKIX_PL_Object_Alloc |
| (PKIX_STRING_TYPE, |
| sizeof (PKIX_PL_String), |
| (PKIX_PL_Object **)&string, |
| plContext), |
| PKIX_COULDNOTALLOCATENEWSTRINGOBJECT); |
| |
| string->utf16String = NULL; |
| string->utf16Length = 0; |
| |
| /* XXX For Debugging */ |
| string->escAsciiString = NULL; |
| string->escAsciiLength = 0; |
| |
| switch (fmtIndicator) { |
| case PKIX_ESCASCII: case PKIX_ESCASCII_DEBUG: |
| PKIX_STRING_DEBUG("\tCalling PL_strlen).\n"); |
| string->escAsciiLength = PL_strlen(stringRep); |
| |
| /* XXX Cache for Debugging */ |
| PKIX_CHECK(PKIX_PL_Malloc |
| ((string->escAsciiLength)+1, |
| (void **)&string->escAsciiString, |
| plContext), |
| PKIX_MALLOCFAILED); |
| |
| (void) PORT_Memcpy |
| (string->escAsciiString, |
| (void *)((char *)stringRep), |
| (string->escAsciiLength)+1); |
| |
| /* Convert the EscASCII string to UTF16 */ |
| PKIX_CHECK(pkix_EscASCII_to_UTF16 |
| (string->escAsciiString, |
| string->escAsciiLength, |
| (fmtIndicator == PKIX_ESCASCII_DEBUG), |
| &string->utf16String, |
| &string->utf16Length, |
| plContext), |
| PKIX_ESCASCIITOUTF16FAILED); |
| break; |
| case PKIX_UTF8: |
| /* Convert the UTF8 string to UTF16 */ |
| PKIX_CHECK(pkix_UTF8_to_UTF16 |
| (stringRep, |
| stringLen, |
| &string->utf16String, |
| &string->utf16Length, |
| plContext), |
| PKIX_UTF8TOUTF16FAILED); |
| break; |
| case PKIX_UTF16: |
| /* UTF16 Strings must be even in length */ |
| if (stringLen%2 == 1) { |
| PKIX_DECREF(string); |
| PKIX_ERROR(PKIX_UTF16ALIGNMENTERROR); |
| } |
| |
| utf16Char = (unsigned char *)stringRep; |
| |
| /* Make sure this is a valid UTF-16 String */ |
| for (i = 0; \ |
| (i < stringLen) && (pkixErrorResult == NULL); \ |
| i += 2) { |
| /* Check that surrogate pairs are valid */ |
| if ((utf16Char[i] >= 0xD8)&& |
| (utf16Char[i] <= 0xDB)) { |
| if ((i+2) >= stringLen) { |
| PKIX_ERROR(PKIX_UTF16HIGHZONEALIGNMENTERROR); |
| /* Second pair should be DC00-DFFF */ |
| } else if (!((utf16Char[i+2] >= 0xDC)&& |
| (utf16Char[i+2] <= 0xDF))) { |
| PKIX_ERROR(PKIX_UTF16LOWZONEERROR); |
| } else { |
| /* Surrogate quartet is valid. */ |
| i += 2; |
| } |
| } |
| } |
| |
| /* Create UTF16 String */ |
| string->utf16Length = stringLen; |
| |
| /* Alloc space for string */ |
| PKIX_CHECK(PKIX_PL_Malloc |
| (stringLen, &string->utf16String, plContext), |
| PKIX_MALLOCFAILED); |
| |
| PKIX_STRING_DEBUG("\tCalling PORT_Memcpy).\n"); |
| (void) PORT_Memcpy |
| (string->utf16String, stringRep, stringLen); |
| break; |
| |
| default: |
| PKIX_ERROR(PKIX_UNKNOWNFORMAT); |
| } |
| |
| *pString = string; |
| |
| cleanup: |
| |
| if (PKIX_ERROR_RECEIVED){ |
| PKIX_DECREF(string); |
| } |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: PKIX_PL_Sprintf (see comments in pkix_pl_system.h) |
| */ |
| PKIX_Error * |
| PKIX_PL_Sprintf( |
| PKIX_PL_String **pOut, |
| void *plContext, |
| const PKIX_PL_String *fmt, |
| ...) |
| { |
| PKIX_PL_String *tempString = NULL; |
| PKIX_UInt32 tempUInt = 0; |
| void *pArg = NULL; |
| char *asciiText = NULL; |
| char *asciiFormat = NULL; |
| char *convertedAsciiFormat = NULL; |
| char *convertedAsciiFormatBase = NULL; |
| va_list args; |
| PKIX_UInt32 length, i, j, dummyLen; |
| |
| PKIX_ENTER(STRING, "PKIX_PL_Sprintf"); |
| PKIX_NULLCHECK_TWO(pOut, fmt); |
| |
| PKIX_CHECK(PKIX_PL_String_GetEncoded |
| ((PKIX_PL_String *)fmt, |
| PKIX_ESCASCII, |
| (void **)&asciiFormat, |
| &length, |
| plContext), |
| PKIX_STRINGGETENCODEDFAILED); |
| |
| PKIX_STRING_DEBUG("\tCalling PR_Malloc).\n"); |
| convertedAsciiFormat = PR_Malloc(length + 1); |
| if (convertedAsciiFormat == NULL) |
| PKIX_ERROR_ALLOC_ERROR(); |
| |
| convertedAsciiFormatBase = convertedAsciiFormat; |
| |
| PKIX_STRING_DEBUG("\tCalling va_start).\n"); |
| va_start(args, fmt); |
| |
| i = 0; |
| j = 0; |
| while (i < length) { |
| if ((asciiFormat[i] == '%')&&((i+1) < length)) { |
| switch (asciiFormat[i+1]) { |
| case 's': |
| convertedAsciiFormat[j++] = asciiFormat[i++]; |
| convertedAsciiFormat[j++] = asciiFormat[i++]; |
| convertedAsciiFormat[j] = '\0'; |
| |
| tempString = va_arg(args, PKIX_PL_String *); |
| if (tempString != NULL) { |
| PKIX_CHECK(PKIX_PL_String_GetEncoded |
| ((PKIX_PL_String*) |
| tempString, |
| PKIX_ESCASCII, |
| &pArg, |
| &dummyLen, |
| plContext), |
| PKIX_STRINGGETENCODEDFAILED); |
| } else { |
| /* there may be a NULL in var_args */ |
| pArg = NULL; |
| } |
| if (asciiText != NULL) { |
| asciiText = PR_sprintf_append(asciiText, |
| (const char *)convertedAsciiFormat, |
| pArg); |
| } else { |
| asciiText = PR_smprintf |
| ((const char *)convertedAsciiFormat, |
| pArg); |
| } |
| if (pArg != NULL) { |
| PKIX_PL_Free(pArg, plContext); |
| pArg = NULL; |
| } |
| convertedAsciiFormat += j; |
| j = 0; |
| break; |
| case 'd': |
| case 'i': |
| case 'o': |
| case 'u': |
| case 'x': |
| case 'X': |
| convertedAsciiFormat[j++] = asciiFormat[i++]; |
| convertedAsciiFormat[j++] = asciiFormat[i++]; |
| convertedAsciiFormat[j] = '\0'; |
| |
| tempUInt = va_arg(args, PKIX_UInt32); |
| if (asciiText != NULL) { |
| asciiText = PR_sprintf_append(asciiText, |
| (const char *)convertedAsciiFormat, |
| tempUInt); |
| } else { |
| asciiText = PR_smprintf |
| ((const char *)convertedAsciiFormat, |
| tempUInt); |
| } |
| convertedAsciiFormat += j; |
| j = 0; |
| break; |
| default: |
| convertedAsciiFormat[j++] = asciiFormat[i++]; |
| convertedAsciiFormat[j++] = asciiFormat[i++]; |
| break; |
| } |
| } else { |
| convertedAsciiFormat[j++] = asciiFormat[i++]; |
| } |
| } |
| |
| /* for constant string value at end of fmt */ |
| if (j > 0) { |
| convertedAsciiFormat[j] = '\0'; |
| if (asciiText != NULL) { |
| asciiText = PR_sprintf_append(asciiText, |
| (const char *)convertedAsciiFormat); |
| } else { |
| asciiText = PR_smprintf((const char *)convertedAsciiFormat); |
| } |
| } |
| |
| va_end(args); |
| |
| /* Copy temporary char * into a string object */ |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, (void *)asciiText, 0, pOut, plContext), |
| PKIX_STRINGCREATEFAILED); |
| |
| cleanup: |
| |
| PKIX_FREE(asciiFormat); |
| |
| if (convertedAsciiFormatBase){ |
| PR_Free(convertedAsciiFormatBase); |
| } |
| |
| if (asciiText){ |
| PKIX_STRING_DEBUG("\tCalling PR_smprintf_free).\n"); |
| PR_smprintf_free(asciiText); |
| } |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: PKIX_PL_GetString (see comments in pkix_pl_system.h) |
| */ |
| PKIX_Error * |
| PKIX_PL_GetString( |
| /* ARGSUSED */ PKIX_UInt32 stringID, |
| char *defaultString, |
| PKIX_PL_String **pString, |
| void *plContext) |
| { |
| PKIX_ENTER(STRING, "PKIX_PL_GetString"); |
| PKIX_NULLCHECK_TWO(pString, defaultString); |
| |
| /* XXX Optimization - use stringID for caching */ |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, defaultString, 0, pString, plContext), |
| PKIX_STRINGCREATEFAILED); |
| |
| cleanup: |
| |
| PKIX_RETURN(STRING); |
| } |
| |
| /* |
| * FUNCTION: PKIX_PL_String_GetEncoded (see comments in pkix_pl_system.h) |
| */ |
| PKIX_Error * |
| PKIX_PL_String_GetEncoded( |
| PKIX_PL_String *string, |
| PKIX_UInt32 fmtIndicator, |
| void **pStringRep, |
| PKIX_UInt32 *pLength, |
| void *plContext) |
| { |
| PKIX_ENTER(STRING, "PKIX_PL_String_GetEncoded"); |
| PKIX_NULLCHECK_THREE(string, pStringRep, pLength); |
| |
| switch (fmtIndicator) { |
| case PKIX_ESCASCII: case PKIX_ESCASCII_DEBUG: |
| PKIX_CHECK(pkix_UTF16_to_EscASCII |
| (string->utf16String, |
| string->utf16Length, |
| (fmtIndicator == PKIX_ESCASCII_DEBUG), |
| (char **)pStringRep, |
| pLength, |
| plContext), |
| PKIX_UTF16TOESCASCIIFAILED); |
| break; |
| case PKIX_UTF8: |
| PKIX_CHECK(pkix_UTF16_to_UTF8 |
| (string->utf16String, |
| string->utf16Length, |
| PKIX_FALSE, |
| pStringRep, |
| pLength, |
| plContext), |
| PKIX_UTF16TOUTF8FAILED); |
| break; |
| case PKIX_UTF8_NULL_TERM: |
| PKIX_CHECK(pkix_UTF16_to_UTF8 |
| (string->utf16String, |
| string->utf16Length, |
| PKIX_TRUE, |
| pStringRep, |
| pLength, |
| plContext), |
| PKIX_UTF16TOUTF8FAILED); |
| break; |
| case PKIX_UTF16: |
| *pLength = string->utf16Length; |
| |
| PKIX_CHECK(PKIX_PL_Malloc(*pLength, pStringRep, plContext), |
| PKIX_MALLOCFAILED); |
| |
| PKIX_STRING_DEBUG("\tCalling PORT_Memcpy).\n"); |
| (void) PORT_Memcpy(*pStringRep, string->utf16String, *pLength); |
| break; |
| default: |
| PKIX_ERROR(PKIX_UNKNOWNFORMAT); |
| } |
| |
| cleanup: |
| |
| PKIX_RETURN(STRING); |
| } |