U_NAMESPACE_BEGIN U_CFUNC UChar U_CALLCONV uregex_utext_unescape_charAt(int32_t offset, void *ct) { struct URegexUTextUnescapeCharContext *context = (struct URegexUTextUnescapeCharContext *)ct; UChar32 c; if (offset == context->lastOffset + 1) { c = UTEXT_NEXT32(context->text); context->lastOffset++; } else if (offset == context->lastOffset) { c = UTEXT_PREVIOUS32(context->text); UTEXT_NEXT32(context->text); } else { utext_moveIndex32(context->text, offset - context->lastOffset - 1); c = UTEXT_NEXT32(context->text); context->lastOffset = offset; } // !!!: Doesn't handle characters outside BMP if (U_IS_BMP(c)) { return (UChar)c; } else { return 0; } }
UChar32 CaseFoldingUTextIterator::next() { UChar32 foldedC; UChar32 originalC; if (fFoldChars == NULL) { // We are not in a string folding of an earlier character. // Start handling the next char from the input UText. originalC = UTEXT_NEXT32(&fUText); if (originalC == U_SENTINEL) { return originalC; } fFoldLength = ucase_toFullFolding(fcsp, originalC, &fFoldChars, U_FOLD_CASE_DEFAULT); if (fFoldLength >= UCASE_MAX_STRING_LENGTH || fFoldLength < 0) { // input code point folds to a single code point, possibly itself. // See comment in ucase.h for explanation of return values from ucase_toFullFoldings. if (fFoldLength < 0) { fFoldLength = ~fFoldLength; } foldedC = (UChar32)fFoldLength; fFoldChars = NULL; return foldedC; } // String foldings fall through here. fFoldIndex = 0; } U16_NEXT(fFoldChars, fFoldIndex, fFoldLength, foldedC); if (fFoldIndex >= fFoldLength) { fFoldChars = NULL; } return foldedC; }
U_CAPI void U_EXPORT2 RegexPatternDump(const RegexPattern *This) { int index; int i; REGEX_DUMP_DEBUG_PRINTF(("Original Pattern: ")); UChar32 c = utext_next32From(This->fPattern, 0); while (c != U_SENTINEL) { if (c<32 || c>256) { c = '.'; } REGEX_DUMP_DEBUG_PRINTF(("%c", c)); c = UTEXT_NEXT32(This->fPattern); } REGEX_DUMP_DEBUG_PRINTF(("\n")); REGEX_DUMP_DEBUG_PRINTF((" Min Match Length: %d\n", This->fMinMatchLen)); REGEX_DUMP_DEBUG_PRINTF((" Match Start Type: %s\n", START_OF_MATCH_STR(This->fStartType))); if (This->fStartType == START_STRING) { REGEX_DUMP_DEBUG_PRINTF((" Initial match string: \"")); for (i=This->fInitialStringIdx; i<This->fInitialStringIdx+This->fInitialStringLen; i++) { REGEX_DUMP_DEBUG_PRINTF(("%c", This->fLiteralText[i])); // TODO: non-printables, surrogates. } REGEX_DUMP_DEBUG_PRINTF(("\"\n")); } else if (This->fStartType == START_SET) { int32_t numSetChars = This->fInitialChars->size(); if (numSetChars > 20) { numSetChars = 20; } REGEX_DUMP_DEBUG_PRINTF((" Match First Chars : ")); for (i=0; i<numSetChars; i++) { UChar32 c = This->fInitialChars->charAt(i); if (0x20<c && c <0x7e) { REGEX_DUMP_DEBUG_PRINTF(("%c ", c)); } else { REGEX_DUMP_DEBUG_PRINTF(("%#x ", c)); } } if (numSetChars < This->fInitialChars->size()) { REGEX_DUMP_DEBUG_PRINTF((" ...")); } REGEX_DUMP_DEBUG_PRINTF(("\n")); } else if (This->fStartType == START_CHAR) { REGEX_DUMP_DEBUG_PRINTF((" First char of Match : ")); if (0x20 < This->fInitialChar && This->fInitialChar<0x7e) { REGEX_DUMP_DEBUG_PRINTF(("%c\n", This->fInitialChar)); } else { REGEX_DUMP_DEBUG_PRINTF(("%#x\n", This->fInitialChar)); } } REGEX_DUMP_DEBUG_PRINTF(("\nIndex Binary Type Operand\n" \ "-------------------------------------------\n")); for (index = 0; index<This->fCompiledPat->size(); index++) { This->dumpOp(index); } REGEX_DUMP_DEBUG_PRINTF(("\n\n")); }
void RegexPattern::dumpPattern() const { #if defined(REGEX_DEBUG) // TODO: This function assumes an ASCII based charset. int index; int i; printf("Original Pattern: "); UChar32 c = utext_next32From(fPattern, 0); while (c != U_SENTINEL) { if (c<32 || c>256) { c = '.'; } printf("%c", c); c = UTEXT_NEXT32(fPattern); } printf("\n"); printf(" Min Match Length: %d\n", fMinMatchLen); printf(" Match Start Type: %s\n", START_OF_MATCH_STR(fStartType)); if (fStartType == START_STRING) { printf(" Initial match string: \""); for (i=fInitialStringIdx; i<fInitialStringIdx+fInitialStringLen; i++) { printf("%c", fLiteralText[i]); // TODO: non-printables, surrogates. } printf("\"\n"); } else if (fStartType == START_SET) { int32_t numSetChars = fInitialChars->size(); if (numSetChars > 20) { numSetChars = 20; } printf(" Match First Chars : "); for (i=0; i<numSetChars; i++) { UChar32 c = fInitialChars->charAt(i); if (0x20<c && c <0x7e) { printf("%c ", c); } else { printf("%#x ", c); } } if (numSetChars < fInitialChars->size()) { printf(" ..."); } printf("\n"); } else if (fStartType == START_CHAR) { printf(" First char of Match : "); if (0x20 < fInitialChar && fInitialChar<0x7e) { printf("%c\n", fInitialChar); } else { printf("%#x\n", fInitialChar); } } printf("Named Capture Groups:\n"); if (uhash_count(fNamedCaptureMap) == 0) { printf(" None\n"); } else { int32_t pos = UHASH_FIRST; const UHashElement *el = NULL; while ((el = uhash_nextElement(fNamedCaptureMap, &pos))) { const UnicodeString *name = (const UnicodeString *)el->key.pointer; char s[100]; name->extract(0, 99, s, sizeof(s), US_INV); // capture group names are invariant. int32_t number = el->value.integer; printf(" %d\t%s\n", number, s); } } printf("\nIndex Binary Type Operand\n" \ "-------------------------------------------\n"); for (index = 0; index<fCompiledPat->size(); index++) { dumpOp(index); } printf("\n\n"); #endif }
//----------------------------------------------------------------------------------- // // handleNext(stateTable) // This method is the actual implementation of the rbbi next() method. // This method initializes the state machine to state 1 // and advances through the text character by character until we reach the end // of the text or the state machine transitions to state 0. We update our return // value every time the state machine passes through an accepting state. // //----------------------------------------------------------------------------------- int32_t BreakIterator::handleNext(const RBBIStateTable *statetable) { int32_t state; int16_t category = 0; RBBIRunMode mode; RBBIStateTableRow *row; UChar32 c; int32_t lookaheadStatus = 0; int32_t lookaheadTagIdx = 0; int32_t result = 0; int32_t initialPosition = 0; int32_t lookaheadResult = 0; UBool lookAheadHardBreak = (statetable->fFlags & RBBI_LOOKAHEAD_HARD_BREAK) != 0; const char *tableData = statetable->fTableData; uint32_t tableRowLen = statetable->fRowLen; #ifdef RBBI_DEBUG if (fTrace) { RBBIDebugPuts("Handle Next pos char state category"); } #endif // No matter what, handleNext alway correctly sets the break tag value. fLastStatusIndexValid = TRUE; fLastRuleStatusIndex = 0; // if we're already at the end of the text, return DONE. initialPosition = (int32_t)UTEXT_GETNATIVEINDEX(fText); result = initialPosition; c = UTEXT_NEXT32(fText); if (fData == NULL || c==U_SENTINEL) { return BreakIterator::DONE; } // Set the initial state for the state machine state = START_STATE; row = (RBBIStateTableRow *) //(statetable->fTableData + (statetable->fRowLen * state)); (tableData + tableRowLen * state); mode = RBBI_RUN; if (statetable->fFlags & RBBI_BOF_REQUIRED) { category = 2; mode = RBBI_START; } // loop until we reach the end of the text or transition to state 0 // for (;;) { if (c == U_SENTINEL) { // Reached end of input string. if (mode == RBBI_END) { // We have already run the loop one last time with the // character set to the psueudo {eof} value. Now it is time // to unconditionally bail out. if (lookaheadResult > result) { // We ran off the end of the string with a pending look-ahead match. // Treat this as if the look-ahead condition had been met, and return // the match at the / position from the look-ahead rule. result = lookaheadResult; fLastRuleStatusIndex = lookaheadTagIdx; lookaheadStatus = 0; } break; } // Run the loop one last time with the fake end-of-input character category. mode = RBBI_END; category = 1; } // // Get the char category. An incoming category of 1 or 2 means that // we are preset for doing the beginning or end of input, and // that we shouldn't get a category from an actual text input character. // if (mode == RBBI_RUN) { // look up the current character's character category, which tells us // which column in the state table to look at. // Note: the 16 in UTRIE_GET16 refers to the size of the data being returned, // not the size of the character going in, which is a UChar32. // UTRIE_GET16(&fTrie, c, category); // Check the dictionary bit in the character's category. // Counter is only used by dictionary based iterators (subclasses). // Chars that need to be handled by a dictionary have a flag bit set // in their category values. // if ((category & 0x4000) != 0) { fDictionaryCharCount++; // And off the dictionary flag bit. category &= ~0x4000; } } #ifdef RBBI_DEBUG if (fTrace) { RBBIDebugPrintf(" %4d ", utext_getNativeIndex(fText)); if (0x20<=c && c<0x7f) { RBBIDebugPrintf("\"%c\" ", c); } else { RBBIDebugPrintf("%5x ", c); } RBBIDebugPrintf("%3d %3d\n", state, category); } #endif // State Transition - move machine to its next state // state = row->fNextState[category]; row = (RBBIStateTableRow *) // (statetable->fTableData + (statetable->fRowLen * state)); (tableData + tableRowLen * state); if (row->fAccepting == -1) { // Match found, common case. if (mode != RBBI_START) { result = (int32_t)UTEXT_GETNATIVEINDEX(fText); } fLastRuleStatusIndex = row->fTagIdx; // Remember the break status (tag) values. } if (row->fLookAhead != 0) { if (lookaheadStatus != 0 && row->fAccepting == lookaheadStatus) { // Lookahead match is completed. result = lookaheadResult; fLastRuleStatusIndex = lookaheadTagIdx; lookaheadStatus = 0; // TODO: make a standalone hard break in a rule work. if (lookAheadHardBreak) { UTEXT_SETNATIVEINDEX(fText, result); return result; } // Look-ahead completed, but other rules may match further. Continue on // TODO: junk this feature? I don't think it's used anywhwere. goto continueOn; } int32_t r = (int32_t)UTEXT_GETNATIVEINDEX(fText); lookaheadResult = r; lookaheadStatus = row->fLookAhead; lookaheadTagIdx = row->fTagIdx; goto continueOn; } if (row->fAccepting != 0) { // Because this is an accepting state, any in-progress look-ahead match // is no longer relavant. Clear out the pending lookahead status. lookaheadStatus = 0; // clear out any pending look-ahead match. } continueOn: if (state == STOP_STATE) { // This is the normal exit from the lookup state machine. // We have advanced through the string until it is certain that no // longer match is possible, no matter what characters follow. break; } // Advance to the next character. // If this is a beginning-of-input loop iteration, don't advance // the input position. The next iteration will be processing the // first real input character. if (mode == RBBI_RUN) { c = UTEXT_NEXT32(fText); } else { if (mode == RBBI_START) { mode = RBBI_RUN; } } } // The state machine is done. Check whether it found a match... // If the iterator failed to advance in the match engine, force it ahead by one. // (This really indicates a defect in the break rules. They should always match // at least one character.) if (result == initialPosition) { UTEXT_SETNATIVEINDEX(fText, initialPosition); UTEXT_NEXT32(fText); result = (int32_t)UTEXT_GETNATIVEINDEX(fText); } // Leave the iterator at our result position. UTEXT_SETNATIVEINDEX(fText, result); #ifdef RBBI_DEBUG if (fTrace) { RBBIDebugPrintf("result = %d\n\n", result); } #endif return result; }
/** * Sets the iterator to refer to the last boundary position before the * specified position. * @offset The position to begin searching for a break from. * @return The position of the last boundary before the starting position. */ int32_t BreakIterator::preceding(int32_t offset) { // if we have cached break positions and offset is in the range // covered by them, use them if (fCachedBreakPositions != NULL) { // TODO: binary search? // TODO: What if offset is outside range, but break is not? if (offset > fCachedBreakPositions[0] && offset <= fCachedBreakPositions[fNumCachedBreakPositions - 1]) { fPositionInCache = 0; while (fPositionInCache < fNumCachedBreakPositions && offset > fCachedBreakPositions[fPositionInCache]) ++fPositionInCache; --fPositionInCache; // If we're at the beginning of the cache, need to reevaluate the // rule status if (fPositionInCache <= 0) { fLastStatusIndexValid = FALSE; } utext_setNativeIndex(fText, fCachedBreakPositions[fPositionInCache]); return fCachedBreakPositions[fPositionInCache]; } else { reset(); } } // if the offset passed in is already past the end of the text, // just return DONE; if it's before the beginning, return the // text's starting offset if (fText == NULL || offset > utext_nativeLength(fText)) { // return BreakIterator::DONE; return last(); } else if (offset < 0) { return first(); } // if we start by updating the current iteration position to the // position specified by the caller, we can just use previous() // to carry out this operation if (fSafeFwdTable != NULL) { // new rule syntax utext_setNativeIndex(fText, offset); int32_t newOffset = (int32_t)UTEXT_GETNATIVEINDEX(fText); if (newOffset != offset) { // Will come here if specified offset was not a code point boundary AND // the underlying implmentation is using UText, which snaps any non-code-point-boundary // indices to the containing code point. // For breakitereator::preceding only, these non-code-point indices need to be moved // up to refer to the following codepoint. UTEXT_NEXT32(fText); offset = (int32_t)UTEXT_GETNATIVEINDEX(fText); } // TODO: (synwee) would it be better to just check for being in the middle of a surrogate pair, // rather than adjusting the position unconditionally? // (Change would interact with safe rules.) // TODO: change RBBI behavior for off-boundary indices to match that of UText? // affects only preceding(), seems cleaner, but is slightly different. UTEXT_PREVIOUS32(fText); handleNext(fSafeFwdTable); int32_t result = (int32_t)UTEXT_GETNATIVEINDEX(fText); while (result >= offset) { result = previous(); } return result; } if (fSafeRevTable != NULL) { // backup plan if forward safe table is not available // TODO: check whether this path can be discarded // It's probably OK to say that rules must supply both safe tables // if they use safe tables at all. We have certainly never described // to anyone how to work with just one safe table. utext_setNativeIndex(fText, offset); UTEXT_NEXT32(fText); // handle previous will give result <= offset handlePrevious(fSafeRevTable); // next will give result 0 or 1 boundary away from offset, // most of the time // we have to int32_t oldresult = next(); while (oldresult < offset) { int32_t result = next(); if (result >= offset) { return oldresult; } oldresult = result; } int32_t result = previous(); if (result >= offset) { return previous(); } return result; } // old rule syntax utext_setNativeIndex(fText, offset); return previous(); }
/** * Sets the iterator to refer to the first boundary position following * the specified position. * @offset The position from which to begin searching for a break position. * @return The position of the first break after the current position. */ int32_t BreakIterator::following(int32_t offset) { // if we have cached break positions and offset is in the range // covered by them, use them // TODO: could use binary search // TODO: what if offset is outside range, but break is not? if (fCachedBreakPositions != NULL) { if (offset >= fCachedBreakPositions[0] && offset < fCachedBreakPositions[fNumCachedBreakPositions - 1]) { fPositionInCache = 0; // We are guaranteed not to leave the array due to range test above while (offset >= fCachedBreakPositions[fPositionInCache]) { ++fPositionInCache; } int32_t pos = fCachedBreakPositions[fPositionInCache]; utext_setNativeIndex(fText, pos); return pos; } else { reset(); } } // if the offset passed in is already past the end of the text, // just return DONE; if it's before the beginning, return the // text's starting offset fLastRuleStatusIndex = 0; fLastStatusIndexValid = TRUE; if (fText == NULL || offset >= utext_nativeLength(fText)) { last(); return next(); } else if (offset < 0) { return first(); } // otherwise, set our internal iteration position (temporarily) // to the position passed in. If this is the _beginning_ position, // then we can just use next() to get our return value int32_t result = 0; if (fSafeRevTable != NULL) { // new rule syntax utext_setNativeIndex(fText, offset); // move forward one codepoint to prepare for moving back to a // safe point. // this handles offset being between a supplementary character UTEXT_NEXT32(fText); // handlePrevious will move most of the time to < 1 boundary away handlePrevious(fSafeRevTable); int32_t result = next(); while (result <= offset) { result = next(); } return result; } if (fSafeFwdTable != NULL) { // backup plan if forward safe table is not available utext_setNativeIndex(fText, offset); UTEXT_PREVIOUS32(fText); // handle next will give result >= offset handleNext(fSafeFwdTable); // previous will give result 0 or 1 boundary away from offset, // most of the time // we have to int32_t oldresult = previous(); while (oldresult > offset) { int32_t result = previous(); if (result <= offset) { return oldresult; } oldresult = result; } int32_t result = next(); if (result <= offset) { return next(); } return result; } // otherwise, we have to sync up first. Use handlePrevious() to back // up to a known break position before the specified position (if // we can determine that the specified position is a break position, // we don't back up at all). This may or may not be the last break // position at or before our starting position. Advance forward // from here until we've passed the starting position. The position // we stop on will be the first break position after the specified one. // old rule syntax utext_setNativeIndex(fText, offset); if (offset==0 || offset==1 && utext_getNativeIndex(fText)==0) { return next(); } result = previous(); while (result != BreakIterator::DONE && result <= offset) { result = next(); } return result; }
static void TestAPI(void) { UErrorCode status = U_ZERO_ERROR; UBool gFailed = FALSE; (void)gFailed; /* Suppress set but not used warning. */ /* Open */ { UText utLoc = UTEXT_INITIALIZER; const char * cString = "\x61\x62\x63\x64"; UChar uString[] = {0x41, 0x42, 0x43, 0}; UText *uta; UText *utb; UChar c; uta = utext_openUChars(NULL, uString, -1, &status); TEST_SUCCESS(status); c = utext_next32(uta); TEST_ASSERT(c == 0x41); utb = utext_close(uta); TEST_ASSERT(utb == NULL); uta = utext_openUTF8(&utLoc, cString, -1, &status); TEST_SUCCESS(status); TEST_ASSERT(uta == &utLoc); uta = utext_close(&utLoc); TEST_ASSERT(uta == &utLoc); } /* utext_clone() */ { UChar uString[] = {0x41, 0x42, 0x43, 0}; int64_t len; UText *uta; UText *utb; status = U_ZERO_ERROR; uta = utext_openUChars(NULL, uString, -1, &status); TEST_SUCCESS(status); utb = utext_clone(NULL, uta, FALSE, FALSE, &status); TEST_SUCCESS(status); TEST_ASSERT(utb != NULL); TEST_ASSERT(utb != uta); len = utext_nativeLength(uta); TEST_ASSERT(len == u_strlen(uString)); utext_close(uta); utext_close(utb); } /* basic access functions */ { UChar uString[] = {0x41, 0x42, 0x43, 0}; UText *uta; UChar32 c; int64_t len; UBool b; int64_t i; status = U_ZERO_ERROR; uta = utext_openUChars(NULL, uString, -1, &status); TEST_ASSERT(uta!=NULL); TEST_SUCCESS(status); b = utext_isLengthExpensive(uta); TEST_ASSERT(b==TRUE); len = utext_nativeLength(uta); TEST_ASSERT(len == u_strlen(uString)); b = utext_isLengthExpensive(uta); TEST_ASSERT(b==FALSE); c = utext_char32At(uta, 0); TEST_ASSERT(c==uString[0]); c = utext_current32(uta); TEST_ASSERT(c==uString[0]); c = utext_next32(uta); TEST_ASSERT(c==uString[0]); c = utext_current32(uta); TEST_ASSERT(c==uString[1]); c = utext_previous32(uta); TEST_ASSERT(c==uString[0]); c = utext_current32(uta); TEST_ASSERT(c==uString[0]); c = utext_next32From(uta, 1); TEST_ASSERT(c==uString[1]); c = utext_next32From(uta, u_strlen(uString)); TEST_ASSERT(c==U_SENTINEL); c = utext_previous32From(uta, 2); TEST_ASSERT(c==uString[1]); i = utext_getNativeIndex(uta); TEST_ASSERT(i == 1); utext_setNativeIndex(uta, 0); b = utext_moveIndex32(uta, 1); TEST_ASSERT(b==TRUE); i = utext_getNativeIndex(uta); TEST_ASSERT(i==1); b = utext_moveIndex32(uta, u_strlen(uString)-1); TEST_ASSERT(b==TRUE); i = utext_getNativeIndex(uta); TEST_ASSERT(i==u_strlen(uString)); b = utext_moveIndex32(uta, 1); TEST_ASSERT(b==FALSE); i = utext_getNativeIndex(uta); TEST_ASSERT(i==u_strlen(uString)); utext_setNativeIndex(uta, 0); c = UTEXT_NEXT32(uta); TEST_ASSERT(c==uString[0]); c = utext_current32(uta); TEST_ASSERT(c==uString[1]); c = UTEXT_PREVIOUS32(uta); TEST_ASSERT(c==uString[0]); c = UTEXT_PREVIOUS32(uta); TEST_ASSERT(c==U_SENTINEL); utext_close(uta); } { /* * UText opened on a NULL string with zero length */ UText *uta; UChar32 c; status = U_ZERO_ERROR; uta = utext_openUChars(NULL, NULL, 0, &status); TEST_SUCCESS(status); c = UTEXT_NEXT32(uta); TEST_ASSERT(c == U_SENTINEL); utext_close(uta); uta = utext_openUTF8(NULL, NULL, 0, &status); TEST_SUCCESS(status); c = UTEXT_NEXT32(uta); TEST_ASSERT(c == U_SENTINEL); utext_close(uta); } { /* * extract */ UText *uta; UChar uString[] = {0x41, 0x42, 0x43, 0}; UChar buf[100]; int32_t i; /* Test pinning of input bounds */ UChar uString2[] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0}; UChar * uString2Ptr = uString2 + 5; status = U_ZERO_ERROR; uta = utext_openUChars(NULL, uString, -1, &status); TEST_SUCCESS(status); status = U_ZERO_ERROR; i = utext_extract(uta, 0, 100, NULL, 0, &status); TEST_ASSERT(status==U_BUFFER_OVERFLOW_ERROR); TEST_ASSERT(i == u_strlen(uString)); status = U_ZERO_ERROR; memset(buf, 0, sizeof(buf)); i = utext_extract(uta, 0, 100, buf, 100, &status); TEST_SUCCESS(status); TEST_ASSERT(i == u_strlen(uString)); i = u_strcmp(uString, buf); TEST_ASSERT(i == 0); utext_close(uta); /* Test pinning of input bounds */ status = U_ZERO_ERROR; uta = utext_openUChars(NULL, uString2Ptr, -1, &status); TEST_SUCCESS(status); status = U_ZERO_ERROR; memset(buf, 0, sizeof(buf)); i = utext_extract(uta, -3, 20, buf, 100, &status); TEST_SUCCESS(status); TEST_ASSERT(i == u_strlen(uString2Ptr)); i = u_strcmp(uString2Ptr, buf); TEST_ASSERT(i == 0); utext_close(uta); } { /* * Copy, Replace, isWritable * Can't create an editable UText from plain C, so all we * can easily do is check that errors returned. */ UText *uta; UChar uString[] = {0x41, 0x42, 0x43, 0}; UBool b; status = U_ZERO_ERROR; uta = utext_openUChars(NULL, uString, -1, &status); TEST_SUCCESS(status); b = utext_isWritable(uta); TEST_ASSERT(b == FALSE); b = utext_hasMetaData(uta); TEST_ASSERT(b == FALSE); utext_replace(uta, 0, 1, /* start, limit */ uString, -1, /* replacement, replacement length */ &status); TEST_ASSERT(status == U_NO_WRITE_PERMISSION); utext_copy(uta, 0, 1, /* start, limit */ 2, /* destination index */ FALSE, /* move flag */ &status); TEST_ASSERT(status == U_NO_WRITE_PERMISSION); utext_close(uta); } }