// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 2004-2015, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************* * file name: uregex.cpp */ #include "unicode/utypes.h" #if !UCONFIG_NO_REGULAR_EXPRESSIONS #include "unicode/regex.h" #include "unicode/uregex.h" #include "unicode/unistr.h" #include "unicode/ustring.h" #include "unicode/uchar.h" #include "unicode/uobject.h" #include "unicode/utf16.h" #include "cmemory.h" #include "uassert.h" #include "uhash.h" #include "umutex.h" #include "uvectr32.h" #include "regextxt.h" U_NAMESPACE_BEGIN #define REMAINING_CAPACITY(idx,len) ((((len)-(idx))>0)?((len)-(idx)):0) struct RegularExpression: public UMemory { public: RegularExpression(); ~RegularExpression(); int32_t fMagic; RegexPattern *fPat; u_atomic_int32_t *fPatRefCount; UChar *fPatString; int32_t fPatStringLen; RegexMatcher *fMatcher; const UChar *fText; // Text from setText() int32_t fTextLength; // Length provided by user with setText(), which // may be -1. UBool fOwnsText; }; static const int32_t REXP_MAGIC = 0x72657870; // "rexp" in ASCII RegularExpression::RegularExpression() { fMagic = REXP_MAGIC; fPat = NULL; fPatRefCount = NULL; fPatString = NULL; fPatStringLen = 0; fMatcher = NULL; fText = NULL; fTextLength = 0; fOwnsText = FALSE; } RegularExpression::~RegularExpression() { delete fMatcher; fMatcher = NULL; if (fPatRefCount!=NULL && umtx_atomic_dec(fPatRefCount)==0) { delete fPat; uprv_free(fPatString); uprv_free((void *)fPatRefCount); } if (fOwnsText && fText!=NULL) { uprv_free((void *)fText); } fMagic = 0; } U_NAMESPACE_END U_NAMESPACE_USE //---------------------------------------------------------------------------------------- // // validateRE Do boilerplate style checks on API function parameters. // Return TRUE if they look OK. //---------------------------------------------------------------------------------------- static UBool validateRE(const RegularExpression *re, UBool requiresText, UErrorCode *status) { if (U_FAILURE(*status)) { return FALSE; } if (re == NULL || re->fMagic != REXP_MAGIC) { *status = U_ILLEGAL_ARGUMENT_ERROR; return FALSE; } // !!! Not sure how to update this with the new UText backing, which is stored in re->fMatcher anyway if (requiresText && re->fText == NULL && !re->fOwnsText) { *status = U_REGEX_INVALID_STATE; return FALSE; } return TRUE; } //---------------------------------------------------------------------------------------- // // uregex_open // //---------------------------------------------------------------------------------------- U_CAPI URegularExpression * U_EXPORT2 uregex_open( const UChar *pattern, int32_t patternLength, uint32_t flags, UParseError *pe, UErrorCode *status) { if (U_FAILURE(*status)) { return NULL; } if (pattern == NULL || patternLength < -1 || patternLength == 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } int32_t actualPatLen = patternLength; if (actualPatLen == -1) { actualPatLen = u_strlen(pattern); } RegularExpression *re = new RegularExpression; u_atomic_int32_t *refC = (u_atomic_int32_t *)uprv_malloc(sizeof(int32_t)); UChar *patBuf = (UChar *)uprv_malloc(sizeof(UChar)*(actualPatLen+1)); if (re == NULL || refC == NULL || patBuf == NULL) { *status = U_MEMORY_ALLOCATION_ERROR; delete re; uprv_free((void *)refC); uprv_free(patBuf); return NULL; } re->fPatRefCount = refC; *re->fPatRefCount = 1; // // Make a copy of the pattern string, so we can return it later if asked. // For compiling the pattern, we will use a UText wrapper around // this local copy, to avoid making even more copies. // re->fPatString = patBuf; re->fPatStringLen = patternLength; u_memcpy(patBuf, pattern, actualPatLen); patBuf[actualPatLen] = 0; UText patText = UTEXT_INITIALIZER; utext_openUChars(&patText, patBuf, patternLength, status); // // Compile the pattern // if (pe != NULL) { re->fPat = RegexPattern::compile(&patText, flags, *pe, *status); } else { re->fPat = RegexPattern::compile(&patText, flags, *status); } utext_close(&patText); if (U_FAILURE(*status)) { goto ErrorExit; } // // Create the matcher object // re->fMatcher = re->fPat->matcher(*status); if (U_SUCCESS(*status)) { return (URegularExpression*)re; } ErrorExit: delete re; return NULL; } //---------------------------------------------------------------------------------------- // // uregex_openUText // //---------------------------------------------------------------------------------------- U_CAPI URegularExpression * U_EXPORT2 uregex_openUText(UText *pattern, uint32_t flags, UParseError *pe, UErrorCode *status) { if (U_FAILURE(*status)) { return NULL; } if (pattern == NULL) { *status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } int64_t patternNativeLength = utext_nativeLength(pattern); if (patternNativeLength == 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } RegularExpression *re = new RegularExpression; UErrorCode lengthStatus = U_ZERO_ERROR; int32_t pattern16Length = utext_extract(pattern, 0, patternNativeLength, NULL, 0, &lengthStatus); u_atomic_int32_t *refC = (u_atomic_int32_t *)uprv_malloc(sizeof(int32_t)); UChar *patBuf = (UChar *)uprv_malloc(sizeof(UChar)*(pattern16Length+1)); if (re == NULL || refC == NULL || patBuf == NULL) { *status = U_MEMORY_ALLOCATION_ERROR; delete re; uprv_free((void *)refC); uprv_free(patBuf); return NULL; } re->fPatRefCount = refC; *re->fPatRefCount = 1; // // Make a copy of the pattern string, so we can return it later if asked. // For compiling the pattern, we will use a read-only UText wrapper // around this local copy, to avoid making even more copies. // re->fPatString = patBuf; re->fPatStringLen = pattern16Length; utext_extract(pattern, 0, patternNativeLength, patBuf, pattern16Length+1, status); UText patText = UTEXT_INITIALIZER; utext_openUChars(&patText, patBuf, pattern16Length, status); // // Compile the pattern // if (pe != NULL) { re->fPat = RegexPattern::compile(&patText, flags, *pe, *status); } else { re->fPat = RegexPattern::compile(&patText, flags, *status); } utext_close(&patText); if (U_FAILURE(*status)) { goto ErrorExit; } // // Create the matcher object // re->fMatcher = re->fPat->matcher(*status); if (U_SUCCESS(*status)) { return (URegularExpression*)re; } ErrorExit: delete re; return NULL; } //---------------------------------------------------------------------------------------- // // uregex_close // //---------------------------------------------------------------------------------------- U_CAPI void U_EXPORT2 uregex_close(URegularExpression *re2) { RegularExpression *re = (RegularExpression*)re2; UErrorCode status = U_ZERO_ERROR; if (validateRE(re, FALSE, &status) == FALSE) { return; } delete re; } //---------------------------------------------------------------------------------------- // // uregex_clone // //---------------------------------------------------------------------------------------- U_CAPI URegularExpression * U_EXPORT2 uregex_clone(const URegularExpression *source2, UErrorCode *status) { RegularExpression *source = (RegularExpression*)source2; if (validateRE(source, FALSE, status) == FALSE) { return NULL; } RegularExpression *clone = new RegularExpression; if (clone == NULL) { *status = U_MEMORY_ALLOCATION_ERROR; return NULL; } clone->fMatcher = source->fPat->matcher(*status); if (U_FAILURE(*status)) { delete clone; return NULL; } clone->fPat = source->fPat; clone->fPatRefCount = source->fPatRefCount; clone->fPatString = source->fPatString; clone->fPatStringLen = source->fPatStringLen; umtx_atomic_inc(source->fPatRefCount); // Note: fText is not cloned. return (URegularExpression*)clone; } //------------------------------------------------------------------------------ // // uregex_pattern // //------------------------------------------------------------------------------ U_CAPI const UChar * U_EXPORT2 uregex_pattern(const URegularExpression *regexp2, int32_t *patLength, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return NULL; } if (patLength != NULL) { *patLength = regexp->fPatStringLen; } return regexp->fPatString; } //------------------------------------------------------------------------------ // // uregex_patternUText // //------------------------------------------------------------------------------ U_CAPI UText * U_EXPORT2 uregex_patternUText(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; return regexp->fPat->patternText(*status); } //------------------------------------------------------------------------------ // // uregex_flags // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_flags(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return 0; } int32_t flags = regexp->fPat->flags(); return flags; } //------------------------------------------------------------------------------ // // uregex_setText // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setText(URegularExpression *regexp2, const UChar *text, int32_t textLength, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return; } if (text == NULL || textLength < -1) { *status = U_ILLEGAL_ARGUMENT_ERROR; return; } if (regexp->fOwnsText && regexp->fText != NULL) { uprv_free((void *)regexp->fText); } regexp->fText = text; regexp->fTextLength = textLength; regexp->fOwnsText = FALSE; UText input = UTEXT_INITIALIZER; utext_openUChars(&input, text, textLength, status); regexp->fMatcher->reset(&input); utext_close(&input); // reset() made a shallow clone, so we don't need this copy } //------------------------------------------------------------------------------ // // uregex_setUText // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setUText(URegularExpression *regexp2, UText *text, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return; } if (text == NULL) { *status = U_ILLEGAL_ARGUMENT_ERROR; return; } if (regexp->fOwnsText && regexp->fText != NULL) { uprv_free((void *)regexp->fText); } regexp->fText = NULL; // only fill it in on request regexp->fTextLength = -1; regexp->fOwnsText = TRUE; regexp->fMatcher->reset(text); } //------------------------------------------------------------------------------ // // uregex_getText // //------------------------------------------------------------------------------ U_CAPI const UChar * U_EXPORT2 uregex_getText(URegularExpression *regexp2, int32_t *textLength, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return NULL; } if (regexp->fText == NULL) { // need to fill in the text UText *inputText = regexp->fMatcher->inputText(); int64_t inputNativeLength = utext_nativeLength(inputText); if (UTEXT_FULL_TEXT_IN_CHUNK(inputText, inputNativeLength)) { regexp->fText = inputText->chunkContents; regexp->fTextLength = (int32_t)inputNativeLength; regexp->fOwnsText = FALSE; // because the UText owns it } else { UErrorCode lengthStatus = U_ZERO_ERROR; regexp->fTextLength = utext_extract(inputText, 0, inputNativeLength, NULL, 0, &lengthStatus); // buffer overflow error UChar *inputChars = (UChar *)uprv_malloc(sizeof(UChar)*(regexp->fTextLength+1)); utext_extract(inputText, 0, inputNativeLength, inputChars, regexp->fTextLength+1, status); regexp->fText = inputChars; regexp->fOwnsText = TRUE; // should already be set but just in case } } if (textLength != NULL) { *textLength = regexp->fTextLength; } return regexp->fText; } //------------------------------------------------------------------------------ // // uregex_getUText // //------------------------------------------------------------------------------ U_CAPI UText * U_EXPORT2 uregex_getUText(URegularExpression *regexp2, UText *dest, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return dest; } return regexp->fMatcher->getInput(dest, *status); } //------------------------------------------------------------------------------ // // uregex_refreshUText // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_refreshUText(URegularExpression *regexp2, UText *text, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return; } regexp->fMatcher->refreshInputText(text, *status); } //------------------------------------------------------------------------------ // // uregex_matches // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_matches(URegularExpression *regexp2, int32_t startIndex, UErrorCode *status) { return uregex_matches64( regexp2, (int64_t)startIndex, status); } U_CAPI UBool U_EXPORT2 uregex_matches64(URegularExpression *regexp2, int64_t startIndex, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; UBool result = FALSE; if (validateRE(regexp, TRUE, status) == FALSE) { return result; } if (startIndex == -1) { result = regexp->fMatcher->matches(*status); } else { result = regexp->fMatcher->matches(startIndex, *status); } return result; } //------------------------------------------------------------------------------ // // uregex_lookingAt // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_lookingAt(URegularExpression *regexp2, int32_t startIndex, UErrorCode *status) { return uregex_lookingAt64( regexp2, (int64_t)startIndex, status); } U_CAPI UBool U_EXPORT2 uregex_lookingAt64(URegularExpression *regexp2, int64_t startIndex, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; UBool result = FALSE; if (validateRE(regexp, TRUE, status) == FALSE) { return result; } if (startIndex == -1) { result = regexp->fMatcher->lookingAt(*status); } else { result = regexp->fMatcher->lookingAt(startIndex, *status); } return result; } //------------------------------------------------------------------------------ // // uregex_find // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_find(URegularExpression *regexp2, int32_t startIndex, UErrorCode *status) { return uregex_find64( regexp2, (int64_t)startIndex, status); } U_CAPI UBool U_EXPORT2 uregex_find64(URegularExpression *regexp2, int64_t startIndex, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; UBool result = FALSE; if (validateRE(regexp, TRUE, status) == FALSE) { return result; } if (startIndex == -1) { regexp->fMatcher->resetPreserveRegion(); result = regexp->fMatcher->find(*status); } else { result = regexp->fMatcher->find(startIndex, *status); } return result; } //------------------------------------------------------------------------------ // // uregex_findNext // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_findNext(URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return FALSE; } UBool result = regexp->fMatcher->find(*status); return result; } //------------------------------------------------------------------------------ // // uregex_groupCount // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_groupCount(URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return 0; } int32_t result = regexp->fMatcher->groupCount(); return result; } //------------------------------------------------------------------------------ // // uregex_groupNumberFromName // //------------------------------------------------------------------------------ int32_t uregex_groupNumberFromName(URegularExpression *regexp2, const UChar *groupName, int32_t nameLength, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return 0; } int32_t result = regexp->fPat->groupNumberFromName(UnicodeString(groupName, nameLength), *status); return result; } int32_t uregex_groupNumberFromCName(URegularExpression *regexp2, const char *groupName, int32_t nameLength, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return 0; } return regexp->fPat->groupNumberFromName(groupName, nameLength, *status); } //------------------------------------------------------------------------------ // // uregex_group // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_group(URegularExpression *regexp2, int32_t groupNum, UChar *dest, int32_t destCapacity, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if (destCapacity < 0 || (destCapacity > 0 && dest == NULL)) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } if (destCapacity == 0 || regexp->fText != NULL) { // If preflighting or if we already have the text as UChars, // this is a little cheaper than extracting from the UText // // Pick up the range of characters from the matcher // int32_t startIx = regexp->fMatcher->start(groupNum, *status); int32_t endIx = regexp->fMatcher->end (groupNum, *status); if (U_FAILURE(*status)) { return 0; } // // Trim length based on buffer capacity // int32_t fullLength = endIx - startIx; int32_t copyLength = fullLength; if (copyLength < destCapacity) { dest[copyLength] = 0; } else if (copyLength == destCapacity) { *status = U_STRING_NOT_TERMINATED_WARNING; } else { copyLength = destCapacity; *status = U_BUFFER_OVERFLOW_ERROR; } // // Copy capture group to user's buffer // if (copyLength > 0) { u_memcpy(dest, ®exp->fText[startIx], copyLength); } return fullLength; } else { int64_t start = regexp->fMatcher->start64(groupNum, *status); int64_t limit = regexp->fMatcher->end64(groupNum, *status); if (U_FAILURE(*status)) { return 0; } // Note edge cases: // Group didn't match: start == end == -1. UText trims to 0, UText gives zero length result. // Zero Length Match: start == end. int32_t length = utext_extract(regexp->fMatcher->inputText(), start, limit, dest, destCapacity, status); return length; } } //------------------------------------------------------------------------------ // // uregex_groupUText // //------------------------------------------------------------------------------ U_CAPI UText * U_EXPORT2 uregex_groupUText(URegularExpression *regexp2, int32_t groupNum, UText *dest, int64_t *groupLength, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { UErrorCode emptyTextStatus = U_ZERO_ERROR; return (dest ? dest : utext_openUChars(NULL, NULL, 0, &emptyTextStatus)); } return regexp->fMatcher->group(groupNum, dest, *groupLength, *status); } //------------------------------------------------------------------------------ // // uregex_start // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_start(URegularExpression *regexp2, int32_t groupNum, UErrorCode *status) { return (int32_t)uregex_start64( regexp2, groupNum, status); } U_CAPI int64_t U_EXPORT2 uregex_start64(URegularExpression *regexp2, int32_t groupNum, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } int32_t result = regexp->fMatcher->start(groupNum, *status); return result; } //------------------------------------------------------------------------------ // // uregex_end // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_end(URegularExpression *regexp2, int32_t groupNum, UErrorCode *status) { return (int32_t)uregex_end64( regexp2, groupNum, status); } U_CAPI int64_t U_EXPORT2 uregex_end64(URegularExpression *regexp2, int32_t groupNum, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } int32_t result = regexp->fMatcher->end(groupNum, *status); return result; } //------------------------------------------------------------------------------ // // uregex_reset // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_reset(URegularExpression *regexp2, int32_t index, UErrorCode *status) { uregex_reset64( regexp2, (int64_t)index, status); } U_CAPI void U_EXPORT2 uregex_reset64(URegularExpression *regexp2, int64_t index, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return; } regexp->fMatcher->reset(index, *status); } //------------------------------------------------------------------------------ // // uregex_setRegion // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setRegion(URegularExpression *regexp2, int32_t regionStart, int32_t regionLimit, UErrorCode *status) { uregex_setRegion64( regexp2, (int64_t)regionStart, (int64_t)regionLimit, status); } U_CAPI void U_EXPORT2 uregex_setRegion64(URegularExpression *regexp2, int64_t regionStart, int64_t regionLimit, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return; } regexp->fMatcher->region(regionStart, regionLimit, *status); } //------------------------------------------------------------------------------ // // uregex_setRegionAndStart // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setRegionAndStart(URegularExpression *regexp2, int64_t regionStart, int64_t regionLimit, int64_t startIndex, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return; } regexp->fMatcher->region(regionStart, regionLimit, startIndex, *status); } //------------------------------------------------------------------------------ // // uregex_regionStart // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_regionStart(const URegularExpression *regexp2, UErrorCode *status) { return (int32_t)uregex_regionStart64(regexp2, status); } U_CAPI int64_t U_EXPORT2 uregex_regionStart64(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } return regexp->fMatcher->regionStart(); } //------------------------------------------------------------------------------ // // uregex_regionEnd // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_regionEnd(const URegularExpression *regexp2, UErrorCode *status) { return (int32_t)uregex_regionEnd64(regexp2, status); } U_CAPI int64_t U_EXPORT2 uregex_regionEnd64(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } return regexp->fMatcher->regionEnd(); } //------------------------------------------------------------------------------ // // uregex_hasTransparentBounds // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_hasTransparentBounds(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return FALSE; } return regexp->fMatcher->hasTransparentBounds(); } //------------------------------------------------------------------------------ // // uregex_useTransparentBounds // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_useTransparentBounds(URegularExpression *regexp2, UBool b, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return; } regexp->fMatcher->useTransparentBounds(b); } //------------------------------------------------------------------------------ // // uregex_hasAnchoringBounds // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_hasAnchoringBounds(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return FALSE; } return regexp->fMatcher->hasAnchoringBounds(); } //------------------------------------------------------------------------------ // // uregex_useAnchoringBounds // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_useAnchoringBounds(URegularExpression *regexp2, UBool b, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status) == FALSE) { return; } regexp->fMatcher->useAnchoringBounds(b); } //------------------------------------------------------------------------------ // // uregex_hitEnd // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_hitEnd(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return FALSE; } return regexp->fMatcher->hitEnd(); } //------------------------------------------------------------------------------ // // uregex_requireEnd // //------------------------------------------------------------------------------ U_CAPI UBool U_EXPORT2 uregex_requireEnd(const URegularExpression *regexp2, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return FALSE; } return regexp->fMatcher->requireEnd(); } //------------------------------------------------------------------------------ // // uregex_setTimeLimit // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setTimeLimit(URegularExpression *regexp2, int32_t limit, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { regexp->fMatcher->setTimeLimit(limit, *status); } } //------------------------------------------------------------------------------ // // uregex_getTimeLimit // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_getTimeLimit(const URegularExpression *regexp2, UErrorCode *status) { int32_t retVal = 0; RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { retVal = regexp->fMatcher->getTimeLimit(); } return retVal; } //------------------------------------------------------------------------------ // // uregex_setStackLimit // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setStackLimit(URegularExpression *regexp2, int32_t limit, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { regexp->fMatcher->setStackLimit(limit, *status); } } //------------------------------------------------------------------------------ // // uregex_getStackLimit // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_getStackLimit(const URegularExpression *regexp2, UErrorCode *status) { int32_t retVal = 0; RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { retVal = regexp->fMatcher->getStackLimit(); } return retVal; } //------------------------------------------------------------------------------ // // uregex_setMatchCallback // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setMatchCallback(URegularExpression *regexp2, URegexMatchCallback *callback, const void *context, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { regexp->fMatcher->setMatchCallback(callback, context, *status); } } //------------------------------------------------------------------------------ // // uregex_getMatchCallback // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_getMatchCallback(const URegularExpression *regexp2, URegexMatchCallback **callback, const void **context, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { regexp->fMatcher->getMatchCallback(*callback, *context, *status); } } //------------------------------------------------------------------------------ // // uregex_setMatchProgressCallback // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_setFindProgressCallback(URegularExpression *regexp2, URegexFindProgressCallback *callback, const void *context, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { regexp->fMatcher->setFindProgressCallback(callback, context, *status); } } //------------------------------------------------------------------------------ // // uregex_getMatchCallback // //------------------------------------------------------------------------------ U_CAPI void U_EXPORT2 uregex_getFindProgressCallback(const URegularExpression *regexp2, URegexFindProgressCallback **callback, const void **context, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, FALSE, status)) { regexp->fMatcher->getFindProgressCallback(*callback, *context, *status); } } //------------------------------------------------------------------------------ // // uregex_replaceAll // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_replaceAll(URegularExpression *regexp2, const UChar *replacementText, int32_t replacementLength, UChar *destBuf, int32_t destCapacity, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if (replacementText == NULL || replacementLength < -1 || (destBuf == NULL && destCapacity > 0) || destCapacity < 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } int32_t len = 0; uregex_reset(regexp2, 0, status); // Note: Seperate error code variables for findNext() and appendReplacement() // are used so that destination buffer overflow errors // in appendReplacement won't stop findNext() from working. // appendReplacement() and appendTail() special case incoming buffer // overflow errors, continuing to return the correct length. UErrorCode findStatus = *status; while (uregex_findNext(regexp2, &findStatus)) { len += uregex_appendReplacement(regexp2, replacementText, replacementLength, &destBuf, &destCapacity, status); } len += uregex_appendTail(regexp2, &destBuf, &destCapacity, status); if (U_FAILURE(findStatus)) { // If anything went wrong with the findNext(), make that error trump // whatever may have happened with the append() operations. // Errors in findNext() are not expected. *status = findStatus; } return len; } //------------------------------------------------------------------------------ // // uregex_replaceAllUText // //------------------------------------------------------------------------------ U_CAPI UText * U_EXPORT2 uregex_replaceAllUText(URegularExpression *regexp2, UText *replacementText, UText *dest, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if (replacementText == NULL) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } dest = regexp->fMatcher->replaceAll(replacementText, dest, *status); return dest; } //------------------------------------------------------------------------------ // // uregex_replaceFirst // //------------------------------------------------------------------------------ U_CAPI int32_t U_EXPORT2 uregex_replaceFirst(URegularExpression *regexp2, const UChar *replacementText, int32_t replacementLength, UChar *destBuf, int32_t destCapacity, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if (replacementText == NULL || replacementLength < -1 || (destBuf == NULL && destCapacity > 0) || destCapacity < 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } int32_t len = 0; UBool findSucceeded; uregex_reset(regexp2, 0, status); findSucceeded = uregex_find(regexp2, 0, status); if (findSucceeded) { len = uregex_appendReplacement(regexp2, replacementText, replacementLength, &destBuf, &destCapacity, status); } len += uregex_appendTail(regexp2, &destBuf, &destCapacity, status); return len; } //------------------------------------------------------------------------------ // // uregex_replaceFirstUText // //------------------------------------------------------------------------------ U_CAPI UText * U_EXPORT2 uregex_replaceFirstUText(URegularExpression *regexp2, UText *replacementText, UText *dest, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if (replacementText == NULL) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } dest = regexp->fMatcher->replaceFirst(replacementText, dest, *status); return dest; } //------------------------------------------------------------------------------ // // uregex_appendReplacement // //------------------------------------------------------------------------------ U_NAMESPACE_BEGIN // // Dummy class, because these functions need to be friends of class RegexMatcher, // and stand-alone C functions don't work as friends // class RegexCImpl { public: inline static int32_t appendReplacement(RegularExpression *regexp, const UChar *replacementText, int32_t replacementLength, UChar **destBuf, int32_t *destCapacity, UErrorCode *status); inline static int32_t appendTail(RegularExpression *regexp, UChar **destBuf, int32_t *destCapacity, UErrorCode *status); inline static int32_t split(RegularExpression *regexp, UChar *destBuf, int32_t destCapacity, int32_t *requiredCapacity, UChar *destFields[], int32_t destFieldsCapacity, UErrorCode *status); }; U_NAMESPACE_END static const UChar BACKSLASH = 0x5c; static const UChar DOLLARSIGN = 0x24; static const UChar LEFTBRACKET = 0x7b; static const UChar RIGHTBRACKET = 0x7d; // // Move a character to an output buffer, with bounds checking on the index. // Index advances even if capacity is exceeded, for preflight size computations. // This little sequence is used a LOT. // static inline void appendToBuf(UChar c, int32_t *idx, UChar *buf, int32_t bufCapacity) { if (*idx < bufCapacity) { buf[*idx] = c; } (*idx)++; } // // appendReplacement, the actual implementation. // int32_t RegexCImpl::appendReplacement(RegularExpression *regexp, const UChar *replacementText, int32_t replacementLength, UChar **destBuf, int32_t *destCapacity, UErrorCode *status) { // If we come in with a buffer overflow error, don't suppress the operation. // A series of appendReplacements, appendTail need to correctly preflight // the buffer size when an overflow happens somewhere in the middle. UBool pendingBufferOverflow = FALSE; if (*status == U_BUFFER_OVERFLOW_ERROR && destCapacity != NULL && *destCapacity == 0) { pendingBufferOverflow = TRUE; *status = U_ZERO_ERROR; } // // Validate all paramters // if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if (replacementText == NULL || replacementLength < -1 || destCapacity == NULL || destBuf == NULL || (*destBuf == NULL && *destCapacity > 0) || *destCapacity < 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } RegexMatcher *m = regexp->fMatcher; if (m->fMatch == FALSE) { *status = U_REGEX_INVALID_STATE; return 0; } UChar *dest = *destBuf; int32_t capacity = *destCapacity; int32_t destIdx = 0; int32_t i; // If it wasn't supplied by the caller, get the length of the replacement text. // TODO: slightly smarter logic in the copy loop could watch for the NUL on // the fly and avoid this step. if (replacementLength == -1) { replacementLength = u_strlen(replacementText); } // Copy input string from the end of previous match to start of current match if (regexp->fText != NULL) { int32_t matchStart; int32_t lastMatchEnd; if (UTEXT_USES_U16(m->fInputText)) { lastMatchEnd = (int32_t)m->fLastMatchEnd; matchStart = (int32_t)m->fMatchStart; } else { // !!!: Would like a better way to do this! UErrorCode tempStatus = U_ZERO_ERROR; lastMatchEnd = utext_extract(m->fInputText, 0, m->fLastMatchEnd, NULL, 0, &tempStatus); tempStatus = U_ZERO_ERROR; matchStart = lastMatchEnd + utext_extract(m->fInputText, m->fLastMatchEnd, m->fMatchStart, NULL, 0, &tempStatus); } for (i=lastMatchEnd; i<matchStart; i++) { appendToBuf(regexp->fText[i], &destIdx, dest, capacity); } } else { UErrorCode possibleOverflowError = U_ZERO_ERROR; // ignore destIdx += utext_extract(m->fInputText, m->fLastMatchEnd, m->fMatchStart, dest==NULL?NULL:&dest[destIdx], REMAINING_CAPACITY(destIdx, capacity), &possibleOverflowError); } U_ASSERT(destIdx >= 0); // scan the replacement text, looking for substitutions ($n) and \escapes. int32_t replIdx = 0; while (replIdx < replacementLength && U_SUCCESS(*status)) { UChar c = replacementText[replIdx]; replIdx++; if (c != DOLLARSIGN && c != BACKSLASH) { // Common case, no substitution, no escaping, // just copy the char to the dest buf. appendToBuf(c, &destIdx, dest, capacity); continue; } if (c == BACKSLASH) { // Backslash Escape. Copy the following char out without further checks. // Note: Surrogate pairs don't need any special handling // The second half wont be a '$' or a '\', and // will move to the dest normally on the next // loop iteration. if (replIdx >= replacementLength) { break; } c = replacementText[replIdx]; if (c==0x55/*U*/ || c==0x75/*u*/) { // We have a \udddd or \Udddddddd escape sequence. UChar32 escapedChar = u_unescapeAt(uregex_ucstr_unescape_charAt, &replIdx, // Index is updated by unescapeAt replacementLength, // Length of replacement text (void *)replacementText); if (escapedChar != (UChar32)0xFFFFFFFF) { if (escapedChar <= 0xffff) { appendToBuf((UChar)escapedChar, &destIdx, dest, capacity); } else { appendToBuf(U16_LEAD(escapedChar), &destIdx, dest, capacity); appendToBuf(U16_TRAIL(escapedChar), &destIdx, dest, capacity); } continue; } // Note: if the \u escape was invalid, just fall through and // treat it as a plain \<anything> escape. } // Plain backslash escape. Just put out the escaped character. appendToBuf(c, &destIdx, dest, capacity); replIdx++; continue; } // We've got a $. Pick up the following capture group name or number. // For numbers, consume only digits that produce a valid capture group for the pattern. int32_t groupNum = 0; U_ASSERT(c == DOLLARSIGN); UChar32 c32 = -1; if (replIdx < replacementLength) { U16_GET(replacementText, 0, replIdx, replacementLength, c32); } if (u_isdigit(c32)) { int32_t numDigits = 0; int32_t numCaptureGroups = m->fPattern->fGroupMap->size(); for (;;) { if (replIdx >= replacementLength) { break; } U16_GET(replacementText, 0, replIdx, replacementLength, c32); if (u_isdigit(c32) == FALSE) { break; } int32_t digitVal = u_charDigitValue(c32); if (groupNum * 10 + digitVal <= numCaptureGroups) { groupNum = groupNum * 10 + digitVal; U16_FWD_1(replacementText, replIdx, replacementLength); numDigits++; } else { if (numDigits == 0) { *status = U_INDEX_OUTOFBOUNDS_ERROR; } break; } } } else if (c32 == LEFTBRACKET) { // Scan for Named Capture Group, ${name}. UnicodeString groupName; U16_FWD_1(replacementText, replIdx, replacementLength); while (U_SUCCESS(*status) && c32 != RIGHTBRACKET) { if (replIdx >= replacementLength) { *status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; break; } U16_NEXT(replacementText, replIdx, replacementLength, c32); if ((c32 >= 0x41 && c32 <= 0x5a) || // A..Z (c32 >= 0x61 && c32 <= 0x7a) || // a..z (c32 >= 0x31 && c32 <= 0x39)) { // 0..9 groupName.append(c32); } else if (c32 == RIGHTBRACKET) { groupNum = uhash_geti(regexp->fPat->fNamedCaptureMap, &groupName); if (groupNum == 0) { // Name not defined by pattern. *status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; } } else { // Character was something other than a name char or a closing '}' *status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; } } } else { // $ not followed by {name} or digits. *status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; } // Finally, append the capture group data to the destination. if (U_SUCCESS(*status)) { destIdx += uregex_group((URegularExpression*)regexp, groupNum, dest==NULL?NULL:&dest[destIdx], REMAINING_CAPACITY(destIdx, capacity), status); if (*status == U_BUFFER_OVERFLOW_ERROR) { // Ignore buffer overflow when extracting the group. We need to // continue on to get full size of the untruncated result. We will // raise our own buffer overflow error at the end. *status = U_ZERO_ERROR; } } if (U_FAILURE(*status)) { // bad group number or name. break; } } // // Nul Terminate the dest buffer if possible. // Set the appropriate buffer overflow or not terminated error, if needed. // if (destIdx < capacity) { dest[destIdx] = 0; } else if (U_SUCCESS(*status)) { if (destIdx == *destCapacity) { *status = U_STRING_NOT_TERMINATED_WARNING; } else { *status = U_BUFFER_OVERFLOW_ERROR; } } // // Return an updated dest buffer and capacity to the caller. // if (destIdx > 0 && *destCapacity > 0) { if (destIdx < capacity) { *destBuf += destIdx; *destCapacity -= destIdx; } else { *destBuf += capacity; *destCapacity = 0; } } // If we came in with a buffer overflow, make sure we go out with one also. // (A zero length match right at the end of the previous match could // make this function succeed even though a previous call had overflowed the buf) if (pendingBufferOverflow && U_SUCCESS(*status)) { *status = U_BUFFER_OVERFLOW_ERROR; } return destIdx; } // // appendReplacement the actual API function, // U_CAPI int32_t U_EXPORT2 uregex_appendReplacement(URegularExpression *regexp2, const UChar *replacementText, int32_t replacementLength, UChar **destBuf, int32_t *destCapacity, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; return RegexCImpl::appendReplacement( regexp, replacementText, replacementLength,destBuf, destCapacity, status); } // // uregex_appendReplacementUText...can just use the normal C++ method // U_CAPI void U_EXPORT2 uregex_appendReplacementUText(URegularExpression *regexp2, UText *replText, UText *dest, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; regexp->fMatcher->appendReplacement(dest, replText, *status); } //------------------------------------------------------------------------------ // // uregex_appendTail // //------------------------------------------------------------------------------ int32_t RegexCImpl::appendTail(RegularExpression *regexp, UChar **destBuf, int32_t *destCapacity, UErrorCode *status) { // If we come in with a buffer overflow error, don't suppress the operation. // A series of appendReplacements, appendTail need to correctly preflight // the buffer size when an overflow happens somewhere in the middle. UBool pendingBufferOverflow = FALSE; if (*status == U_BUFFER_OVERFLOW_ERROR && destCapacity != NULL && *destCapacity == 0) { pendingBufferOverflow = TRUE; *status = U_ZERO_ERROR; } if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if (destCapacity == NULL || destBuf == NULL || (*destBuf == NULL && *destCapacity > 0) || *destCapacity < 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } RegexMatcher *m = regexp->fMatcher; int32_t destIdx = 0; int32_t destCap = *destCapacity; UChar *dest = *destBuf; if (regexp->fText != NULL) { int32_t srcIdx; int64_t nativeIdx = (m->fMatch ? m->fMatchEnd : m->fLastMatchEnd); if (nativeIdx == -1) { srcIdx = 0; } else if (UTEXT_USES_U16(m->fInputText)) { srcIdx = (int32_t)nativeIdx; } else { UErrorCode newStatus = U_ZERO_ERROR; srcIdx = utext_extract(m->fInputText, 0, nativeIdx, NULL, 0, &newStatus); } for (;;) { U_ASSERT(destIdx >= 0); if (srcIdx == regexp->fTextLength) { break; } UChar c = regexp->fText[srcIdx]; if (c == 0 && regexp->fTextLength == -1) { regexp->fTextLength = srcIdx; break; } if (destIdx < destCap) { dest[destIdx] = c; } else { // We've overflowed the dest buffer. // If the total input string length is known, we can // compute the total buffer size needed without scanning through the string. if (regexp->fTextLength > 0) { destIdx += (regexp->fTextLength - srcIdx); break; } } srcIdx++; destIdx++; } } else { int64_t srcIdx; if (m->fMatch) { // The most recent call to find() succeeded. srcIdx = m->fMatchEnd; } else { // The last call to find() on this matcher failed(). // Look back to the end of the last find() that succeeded for src index. srcIdx = m->fLastMatchEnd; if (srcIdx == -1) { // There has been no successful match with this matcher. // We want to copy the whole string. srcIdx = 0; } } destIdx = utext_extract(m->fInputText, srcIdx, m->fInputLength, dest, destCap, status); } // // NUL terminate the output string, if possible, otherwise issue the // appropriate error or warning. // if (destIdx < destCap) { dest[destIdx] = 0; } else if (destIdx == destCap) { *status = U_STRING_NOT_TERMINATED_WARNING; } else { *status = U_BUFFER_OVERFLOW_ERROR; } // // Update the user's buffer ptr and capacity vars to reflect the // amount used. // if (destIdx < destCap) { *destBuf += destIdx; *destCapacity -= destIdx; } else if (*destBuf != NULL) { *destBuf += destCap; *destCapacity = 0; } if (pendingBufferOverflow && U_SUCCESS(*status)) { *status = U_BUFFER_OVERFLOW_ERROR; } return destIdx; } // // appendTail the actual API function // U_CAPI int32_t U_EXPORT2 uregex_appendTail(URegularExpression *regexp2, UChar **destBuf, int32_t *destCapacity, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; return RegexCImpl::appendTail(regexp, destBuf, destCapacity, status); } // // uregex_appendTailUText...can just use the normal C++ method // U_CAPI UText * U_EXPORT2 uregex_appendTailUText(URegularExpression *regexp2, UText *dest, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; return regexp->fMatcher->appendTail(dest, *status); } //------------------------------------------------------------------------------ // // copyString Internal utility to copy a string to an output buffer, // while managing buffer overflow and preflight size // computation. NUL termination is added to destination, // and the NUL is counted in the output size. // //------------------------------------------------------------------------------ #if 0 static void copyString(UChar *destBuffer, // Destination buffer. int32_t destCapacity, // Total capacity of dest buffer int32_t *destIndex, // Index into dest buffer. Updated on return. // Update not clipped to destCapacity. const UChar *srcPtr, // Pointer to source string int32_t srcLen) // Source string len. { int32_t si; int32_t di = *destIndex; UChar c; for (si=0; si<srcLen; si++) { c = srcPtr[si]; if (di < destCapacity) { destBuffer[di] = c; di++; } else { di += srcLen - si; break; } } if (di<destCapacity) { destBuffer[di] = 0; } di++; *destIndex = di; } #endif //------------------------------------------------------------------------------ // // uregex_split // //------------------------------------------------------------------------------ int32_t RegexCImpl::split(RegularExpression *regexp, UChar *destBuf, int32_t destCapacity, int32_t *requiredCapacity, UChar *destFields[], int32_t destFieldsCapacity, UErrorCode *status) { // // Reset for the input text // regexp->fMatcher->reset(); UText *inputText = regexp->fMatcher->fInputText; int64_t nextOutputStringStart = 0; int64_t inputLen = regexp->fMatcher->fInputLength; if (inputLen == 0) { return 0; } // // Loop through the input text, searching for the delimiter pattern // int32_t i; // Index of the field being processed. int32_t destIdx = 0; // Next available position in destBuf; int32_t numCaptureGroups = regexp->fMatcher->groupCount(); UErrorCode tStatus = U_ZERO_ERROR; // Want to ignore any buffer overflow errors so that the strings are still counted for (i=0; ; i++) { if (i>=destFieldsCapacity-1) { // There are one or zero output strings left. // Fill the last output string with whatever is left from the input, then exit the loop. // ( i will be == destFieldsCapacity if we filled the output array while processing // capture groups of the delimiter expression, in which case we will discard the // last capture group saved in favor of the unprocessed remainder of the // input string.) if (inputLen > nextOutputStringStart) { if (i != destFieldsCapacity-1) { // No fields are left. Recycle the last one for holding the trailing part of // the input string. i = destFieldsCapacity-1; destIdx = (int32_t)(destFields[i] - destFields[0]); } destFields[i] = &destBuf[destIdx]; destIdx += 1 + utext_extract(inputText, nextOutputStringStart, inputLen, &destBuf[destIdx], REMAINING_CAPACITY(destIdx, destCapacity), status); } break; } if (regexp->fMatcher->find()) { // We found another delimiter. Move everything from where we started looking // up until the start of the delimiter into the next output string. destFields[i] = &destBuf[destIdx]; destIdx += 1 + utext_extract(inputText, nextOutputStringStart, regexp->fMatcher->fMatchStart, &destBuf[destIdx], REMAINING_CAPACITY(destIdx, destCapacity), &tStatus); if (tStatus == U_BUFFER_OVERFLOW_ERROR) { tStatus = U_ZERO_ERROR; } else { *status = tStatus; } nextOutputStringStart = regexp->fMatcher->fMatchEnd; // If the delimiter pattern has capturing parentheses, the captured // text goes out into the next n destination strings. int32_t groupNum; for (groupNum=1; groupNum<=numCaptureGroups; groupNum++) { // If we've run out of output string slots, bail out. if (i==destFieldsCapacity-1) { break; } i++; // Set up to extract the capture group contents into the dest buffer. destFields[i] = &destBuf[destIdx]; tStatus = U_ZERO_ERROR; int32_t t = uregex_group((URegularExpression*)regexp, groupNum, destFields[i], REMAINING_CAPACITY(destIdx, destCapacity), &tStatus); destIdx += t + 1; // Record the space used in the output string buffer. // +1 for the NUL that terminates the string. if (tStatus == U_BUFFER_OVERFLOW_ERROR) { tStatus = U_ZERO_ERROR; } else { *status = tStatus; } } if (nextOutputStringStart == inputLen) { // The delimiter was at the end of the string. // Output an empty string, and then we are done. if (destIdx < destCapacity) { destBuf[destIdx] = 0; } if (i < destFieldsCapacity-1) { ++i; } if (destIdx < destCapacity) { destFields[i] = destBuf + destIdx; } ++destIdx; break; } } else { // We ran off the end of the input while looking for the next delimiter. // All the remaining text goes into the current output string. destFields[i] = &destBuf[destIdx]; destIdx += 1 + utext_extract(inputText, nextOutputStringStart, inputLen, &destBuf[destIdx], REMAINING_CAPACITY(destIdx, destCapacity), status); break; } } // Zero out any unused portion of the destFields array int j; for (j=i+1; j<destFieldsCapacity; j++) { destFields[j] = NULL; } if (requiredCapacity != NULL) { *requiredCapacity = destIdx; } if (destIdx > destCapacity) { *status = U_BUFFER_OVERFLOW_ERROR; } return i+1; } // // uregex_split The actual API function // U_CAPI int32_t U_EXPORT2 uregex_split(URegularExpression *regexp2, UChar *destBuf, int32_t destCapacity, int32_t *requiredCapacity, UChar *destFields[], int32_t destFieldsCapacity, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; if (validateRE(regexp, TRUE, status) == FALSE) { return 0; } if ((destBuf == NULL && destCapacity > 0) || destCapacity < 0 || destFields == NULL || destFieldsCapacity < 1 ) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } return RegexCImpl::split(regexp, destBuf, destCapacity, requiredCapacity, destFields, destFieldsCapacity, status); } // // uregex_splitUText...can just use the normal C++ method // U_CAPI int32_t U_EXPORT2 uregex_splitUText(URegularExpression *regexp2, UText *destFields[], int32_t destFieldsCapacity, UErrorCode *status) { RegularExpression *regexp = (RegularExpression*)regexp2; return regexp->fMatcher->split(regexp->fMatcher->inputText(), destFields, destFieldsCapacity, *status); } #endif // !UCONFIG_NO_REGULAR_EXPRESSIONS