2
******************************************************************************
4
* Copyright (C) 1998-2001, International Business Machines
5
* Corporation and others. All Rights Reserved.
7
******************************************************************************
11
* Modification History:
13
* Date Name Description
14
* 12/07/98 bertrand Creation.
15
******************************************************************************
18
#include "unicode/utypes.h"
19
#include "unicode/ustring.h"
20
#include "unicode/putil.h"
21
#include "unicode/ucnv.h"
29
/* forward declaractions of definitions for the shared default converter */
31
static UConverter *gDefaultConverter = NULL;
33
/* ANSI string.h - style functions ------------------------------------------ */
35
#define MAX_STRLEN 0x0FFFFFFF
37
/* ---- String searching functions ---- */
39
U_CAPI UChar* U_EXPORT2
40
u_strchr(const UChar *s, UChar c)
42
while (*s && *s != c) {
50
/* A Boyer-Moore algorithm would be better, but that would require a hashtable
51
because UChar is so big. This algorithm doesn't use a lot of extra memory.
53
U_CAPI UChar * U_EXPORT2
54
u_strstr(const UChar *s, const UChar *substring) {
56
UChar *strItr, *subItr;
58
if (*substring == 0) {
64
subItr = (UChar *)substring;
66
/* Only one string iterator needs checking for null terminator */
67
while ((*strItr != 0) && (*strItr == *subItr)) {
72
if (*subItr == 0) { /* Was the end of the substring reached? */
77
} while (*strItr != 0); /* Was the end of the string reached? */
79
return NULL; /* No match */
83
* Check if there is an unmatched surrogate c in a string [start..limit[ at s.
84
* start<=s<limit or limit==NULL
85
* @return TRUE if *s is unmatched
88
uprv_isSingleSurrogate(const UChar *start, const UChar *s, UChar c, const UChar *limit) {
89
if(UTF_IS_SURROGATE_FIRST(c)) {
91
return (UBool)(s==limit || !UTF_IS_TRAIL(*s));
93
return (UBool)(s==start || !UTF_IS_LEAD(*(s-1)));
98
uprv_strFindSurrogate(const UChar *s, int32_t length, UChar surrogate) {
99
const UChar *limit, *t;
108
for(t=s; t!=limit && ((c=*t)!=0 || limit!=NULL); ++t) {
109
if(c==surrogate && uprv_isSingleSurrogate(s, t, c, limit)) {
117
U_CFUNC const UChar *
118
uprv_strFindLastSurrogate(const UChar *s, int32_t length, UChar surrogate) {
119
const UChar *limit, *t;
128
for(t=limit; t!=s;) {
130
if(c==surrogate && uprv_isSingleSurrogate(s, t, c, limit)) {
138
U_CAPI UChar * U_EXPORT2
139
u_strchr32(const UChar *s, UChar32 c) {
141
/* non-surrogate BMP code point */
142
return u_strchr(s, (UChar)c);
143
} else if(c <= 0xdfff) {
144
/* surrogate code point */
145
return (UChar *)uprv_strFindSurrogate(s, -1, (UChar)c);
146
} else if(c <= 0xffff) {
147
/* non-surrogate BMP code point */
148
return u_strchr(s, (UChar)c);
150
/* supplementary code point, search for string */
153
buffer[0] = UTF16_LEAD(c);
154
buffer[1] = UTF16_TRAIL(c);
156
return u_strstr(s, buffer);
160
/* Search for a codepoint in a string that matches one of the matchSet codepoints. */
161
U_CAPI UChar * U_EXPORT2
162
u_strpbrk(const UChar *string, const UChar *matchSet)
167
for (matchLen = 0; matchSet[matchLen]; matchLen++)
169
if (!UTF_IS_SINGLE(matchSet[matchLen]))
177
const UChar *matchItr;
180
for (strItr = string; *strItr; strItr++)
182
for (matchItr = matchSet; *matchItr; matchItr++)
184
if (*matchItr == *strItr)
186
return (UChar *)strItr;
195
UChar32 stringCh, matchSetCh;
196
int32_t stringLen = u_strlen(string);
198
for (strItr = 0; strItr < stringLen; strItr++)
200
UTF_GET_CHAR_SAFE(string, 0, strItr, stringLen, stringCh, TRUE);
201
for (matchItr = 0; matchItr < matchLen; matchItr++)
203
UTF_GET_CHAR_SAFE(matchSet, 0, matchItr, matchLen, matchSetCh, TRUE);
204
if (stringCh == matchSetCh && (stringCh != UTF_ERROR_VALUE
205
|| string[strItr] == UTF_ERROR_VALUE
206
|| (matchSetCh == UTF_ERROR_VALUE && !UTF_IS_SINGLE(matchSet[matchItr]))))
208
return (UChar *)string + strItr;
214
/* Didn't find it. */
218
/* Search for a codepoint in a string that matches one of the matchSet codepoints. */
219
U_CAPI int32_t U_EXPORT2
220
u_strcspn(const UChar *string, const UChar *matchSet)
222
const UChar *foundStr = u_strpbrk(string, matchSet);
223
if (foundStr == NULL)
225
return u_strlen(string);
227
return foundStr - string;
230
/* Search for a codepoint in a string that does not match one of the matchSet codepoints. */
231
U_CAPI int32_t U_EXPORT2
232
u_strspn(const UChar *string, const UChar *matchSet)
239
for (matchLen = 0; matchSet[matchLen]; matchLen++)
241
if (!UTF_IS_SINGLE(matchSet[matchLen]))
249
const UChar *matchItr;
252
for (strItr = string; *strItr && match; strItr++)
255
for (matchItr = matchSet; *matchItr; matchItr++)
257
if (*matchItr == *strItr)
264
retValue = strItr - string - (match == FALSE);
270
UChar32 stringCh, matchSetCh;
271
int32_t stringLen = u_strlen(string);
273
for (strItr = 0; strItr < stringLen && match; strItr++)
276
UTF_GET_CHAR_SAFE(string, 0, strItr, stringLen, stringCh, TRUE);
277
for (matchItr = 0; matchItr < matchLen; matchItr++)
279
UTF_GET_CHAR_SAFE(matchSet, 0, matchItr, matchLen, matchSetCh, TRUE);
280
if (stringCh == matchSetCh && (stringCh != UTF_ERROR_VALUE
281
|| string[strItr] == UTF_ERROR_VALUE
282
|| (matchSetCh == UTF_ERROR_VALUE && !UTF_IS_SINGLE(matchSet[matchItr]))))
289
retValue = strItr - (match == FALSE);
292
/* Found a mismatch or didn't find it. */
296
/* ----- Text manipulation functions --- */
298
U_CAPI UChar* U_EXPORT2
299
u_strtok_r(UChar *src,
305
uint32_t nonDelimIdx;
307
/* If saveState is NULL, the user messed up. */
310
*saveState = src; /* Set to "src" in case there are no delimiters */
312
else if (*saveState) {
313
tokSource = *saveState;
316
/* src == NULL && *saveState == NULL */
317
/* This shouldn't happen. We already finished tokenizing. */
321
/* Skip initial delimiters */
322
nonDelimIdx = u_strspn(tokSource, delim);
323
tokSource = &tokSource[nonDelimIdx];
326
nextToken = u_strpbrk(tokSource, delim);
327
if (nextToken != NULL) {
330
*saveState = nextToken;
333
else if (*saveState) {
334
/* Return the last token */
340
/* No tokens were found. Only delimiters were left. */
346
U_CAPI UChar* U_EXPORT2
350
UChar *anchor = dst; /* save a pointer to start of dst */
352
while(*dst != 0) { /* To end of first string */
355
while((*(dst++) = *(src++)) != 0) { /* copy string 2 over */
361
U_CAPI UChar* U_EXPORT2
362
u_strncat(UChar *dst,
367
UChar *anchor = dst; /* save a pointer to start of dst */
369
while(*dst != 0) { /* To end of first string */
372
while((*dst = *src) != 0) { /* copy string 2 over */
387
/* ----- Text property functions --- */
389
U_CAPI int32_t U_EXPORT2
390
u_strcmp(const UChar *s1,
398
if (c1 != c2 || c1 == 0) {
402
return (int32_t)c1 - (int32_t)c2;
405
/* rotate surrogates to the top to get code point order; assume c>=0xd800 */
406
#define UTF16FIXUP(c) { \
407
if ((c) >= 0xe000) { \
415
/* String compare in code point order - u_strcmp() compares in code unit order. */
416
U_CAPI int32_t U_EXPORT2
417
u_strcmpCodePointOrder(const UChar *s1, const UChar *s2) {
420
/* compare identical prefixes - they do not need to be fixed up */
432
/* if both values are in or above the surrogate range, Fix them up. */
433
if (c1 >= 0xD800 && c2 >= 0xD800) {
438
/* now c1 and c2 are in UTF-32-compatible order */
439
return (int32_t)c1-(int32_t)c2;
442
U_CAPI int32_t U_EXPORT2
443
u_strncmp(const UChar *s1,
450
rc = (int32_t)*s1 - (int32_t)*s2;
451
if(rc != 0 || *s1 == 0 || --n == 0) {
462
U_CAPI int32_t U_EXPORT2
463
u_strncmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t n) {
470
/* compare identical prefixes - they do not need to be fixed up */
475
if(c1==0 || --n==0) {
485
/* c1!=c2, fix up each one if they're both in or above the surrogate range, then compare them */
486
if (c1 >= 0xD800 && c2 >= 0xD800) {
491
/* now c1 and c2 are in UTF-32-compatible order */
492
return (int32_t)c1-(int32_t)c2;
495
U_CAPI UChar* U_EXPORT2
499
UChar *anchor = dst; /* save a pointer to start of dst */
501
while((*(dst++) = *(src++)) != 0) { /* copy string 2 over */
507
U_CAPI UChar* U_EXPORT2
508
u_strncpy(UChar *dst,
512
UChar *anchor = dst; /* save a pointer to start of dst */
514
/* copy string 2 over */
515
while(n > 0 && (*(dst++) = *(src++)) != 0) {
522
U_CAPI int32_t U_EXPORT2
523
u_strlen(const UChar *s)
525
#if U_SIZEOF_WCHAR_T == U_SIZEOF_UCHAR
526
return uprv_wcslen(s);
536
U_CAPI int32_t U_EXPORT2
537
u_countChar32(const UChar *s, int32_t length) {
540
if(s==NULL || length<-1) {
548
if(UTF_IS_LEAD(*s) && length>=2 && UTF_IS_TRAIL(*(s+1))) {
556
} else /* length==-1 */ {
566
* sufficient to look ahead one because of UTF-16;
567
* safe to look ahead one because at worst that would be the terminating NUL
569
if(UTF_IS_LEAD(c) && UTF_IS_TRAIL(*s)) {
577
U_CAPI UChar * U_EXPORT2
578
u_memcpy(UChar *dest, const UChar *src, int32_t count) {
579
return (UChar *)uprv_memcpy(dest, src, count*U_SIZEOF_UCHAR);
582
U_CAPI UChar * U_EXPORT2
583
u_memmove(UChar *dest, const UChar *src, int32_t count) {
584
return (UChar *)uprv_memmove(dest, src, count*U_SIZEOF_UCHAR);
587
U_CAPI UChar * U_EXPORT2
588
u_memset(UChar *dest, UChar c, int32_t count) {
591
UChar *limit = dest + count;
593
while (ptr < limit) {
600
U_CAPI int32_t U_EXPORT2
601
u_memcmp(const UChar *buf1, const UChar *buf2, int32_t count) {
603
const UChar *limit = buf1 + count;
606
while (buf1 < limit) {
607
result = (int32_t)(uint16_t)*buf1 - (int32_t)(uint16_t)*buf2;
618
U_CAPI int32_t U_EXPORT2
619
u_memcmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t count) {
629
/* compare identical prefixes - they do not need to be fixed up */
643
/* c1!=c2, fix up each one if they're both in or above the surrogate range, then compare them */
644
if (c1 >= 0xD800 && c2 >= 0xD800) {
649
/* now c1 and c2 are in UTF-32-compatible order */
650
return (int32_t)c1-(int32_t)c2;
653
U_CAPI UChar * U_EXPORT2
654
u_memchr(const UChar *src, UChar ch, int32_t count) {
656
const UChar *ptr = src;
657
const UChar *limit = src + count;
663
} while (++ptr < limit);
668
U_CAPI UChar * U_EXPORT2
669
u_memchr32(const UChar *src, UChar32 ch, int32_t count) {
670
if(count<=0 || (uint32_t)ch>0x10ffff) {
671
return NULL; /* no string, or illegal arguments */
675
/* non-surrogate BMP code point */
676
return u_memchr(src, (UChar)ch, count); /* BMP, single UChar */
677
} else if(ch<=0xdfff) {
678
/* surrogate code point */
679
return (UChar *)uprv_strFindSurrogate(src, count, (UChar)ch);
680
} else if(ch<=0xffff) {
681
return u_memchr(src, (UChar)ch, count); /* BMP, single UChar */
683
return NULL; /* too short for a surrogate pair */
685
const UChar *limit=src+count-1; /* -1 so that we do not need a separate check for the trail unit */
686
UChar lead=UTF16_LEAD(ch), trail=UTF16_TRAIL(ch);
689
if(*src==lead && *(src+1)==trail) {
692
} while(++src<limit);
697
/* conversions between char* and UChar* ------------------------------------- */
700
returns the minimum of (the length of the null-terminated string) and n.
702
static int32_t u_astrnlen(const char *s1, int32_t n)
708
while (*(s1++) && n--)
716
U_CAPI UChar* U_EXPORT2
717
u_uastrncpy(UChar *ucs1,
721
UChar *target = ucs1;
722
UErrorCode err = U_ZERO_ERROR;
723
UConverter *cnv = u_getDefaultConverter(&err);
724
if(U_SUCCESS(err) && cnv != NULL) {
730
s2+u_astrnlen(s2, n),
734
ucnv_reset(cnv); /* be good citizens */
735
u_releaseDefaultConverter(cnv);
736
if(U_FAILURE(err) && (err != U_BUFFER_OVERFLOW_ERROR) ) {
737
*ucs1 = 0; /* failure */
739
if(target < (ucs1+n)) { /* U_BUFFER_OVERFLOW_ERROR isn't an err, just means no termination will happen. */
740
*target = 0; /* terminate */
748
U_CAPI UChar* U_EXPORT2
749
u_uastrcpy(UChar *ucs1,
752
UErrorCode err = U_ZERO_ERROR;
753
UConverter *cnv = u_getDefaultConverter(&err);
754
if(U_SUCCESS(err) && cnv != NULL) {
761
u_releaseDefaultConverter(cnv);
772
returns the minimum of (the length of the null-terminated string) and n.
774
static int32_t u_ustrnlen(const UChar *ucs1, int32_t n)
780
while (*(ucs1++) && n--)
788
U_CAPI char* U_EXPORT2
789
u_austrncpy(char *s1,
794
UErrorCode err = U_ZERO_ERROR;
795
UConverter *cnv = u_getDefaultConverter(&err);
796
if(U_SUCCESS(err) && cnv != NULL) {
798
ucnv_fromUnicode(cnv,
802
ucs2+u_ustrnlen(ucs2, n),
806
ucnv_reset(cnv); /* be good citizens */
807
u_releaseDefaultConverter(cnv);
808
if(U_FAILURE(err) && (err != U_BUFFER_OVERFLOW_ERROR) ) {
809
*s1 = 0; /* failure */
811
if(target < (s1+n)) { /* U_BUFFER_OVERFLOW_ERROR isn't an err, just means no termination will happen. */
812
*target = 0; /* terminate */
820
U_CAPI char* U_EXPORT2
824
UErrorCode err = U_ZERO_ERROR;
825
UConverter *cnv = u_getDefaultConverter(&err);
826
if(U_SUCCESS(err) && cnv != NULL) {
827
int32_t len = ucnv_fromUChars(cnv,
833
u_releaseDefaultConverter(cnv);
841
/* mutexed access to a shared default converter ----------------------------- */
843
UBool ustring_cleanup(void) {
844
if (gDefaultConverter) {
845
ucnv_close(gDefaultConverter);
846
gDefaultConverter = NULL;
849
/* it's safe to close a 0 converter */
853
U_CAPI UConverter* U_EXPORT2
854
u_getDefaultConverter(UErrorCode *status)
856
UConverter *converter = NULL;
858
if (gDefaultConverter != NULL) {
861
/* need to check to make sure it wasn't taken out from under us */
862
if (gDefaultConverter != NULL) {
863
converter = gDefaultConverter;
864
gDefaultConverter = NULL;
869
/* if the cache was empty, create a converter */
870
if(converter == NULL) {
871
converter = ucnv_open(NULL, status);
872
if(U_FAILURE(*status)) {
880
U_CAPI void U_EXPORT2
881
u_releaseDefaultConverter(UConverter *converter)
883
if(gDefaultConverter == NULL) {
884
if (converter != NULL) {
885
ucnv_reset(converter);
889
if(gDefaultConverter == NULL) {
890
gDefaultConverter = converter;
896
if(converter != NULL) {
897
ucnv_close(converter);
901
/* u_unescape & support fns ------------------------------------------------- */
903
/* This map must be in ASCENDING ORDER OF THE ESCAPE CODE */
904
static const UChar UNESCAPE_MAP[] = {
917
enum { UNESCAPE_MAP_LENGTH = sizeof(UNESCAPE_MAP) / sizeof(UNESCAPE_MAP[0]) };
919
/* Convert one octal digit to a numeric value 0..7, or -1 on failure */
920
static int8_t _digit8(UChar c) {
921
if (c >= 0x0030 && c <= 0x0037) {
922
return (int8_t)(c - 0x0030);
927
/* Convert one hex digit to a numeric value 0..F, or -1 on failure */
928
static int8_t _digit16(UChar c) {
929
if (c >= 0x0030 && c <= 0x0039) {
930
return (int8_t)(c - 0x0030);
932
if (c >= 0x0041 && c <= 0x0046) {
933
return (int8_t)(c - (0x0041 - 10));
935
if (c >= 0x0061 && c <= 0x0066) {
936
return (int8_t)(c - (0x0061 - 10));
941
/* Parse a single escape sequence. Although this method deals in
942
* UChars, it does not use C++ or UnicodeString. This allows it to
943
* be used from C contexts. */
944
U_CAPI UChar32 U_EXPORT2
945
u_unescapeAt(UNESCAPE_CHAR_AT charAt,
950
int32_t start = *offset;
956
int8_t bitsPerDigit = 4;
960
/* Check that offset is in range */
961
if (*offset < 0 || *offset >= length) {
965
/* Fetch first UChar after '\\' */
966
c = charAt((*offset)++, context);
968
/* Convert hexadecimal and octal escapes */
985
n = 1; /* Already have first octal digit */
992
while (*offset < length && n < maxDig) {
993
c = charAt(*offset, context);
994
dig = (int8_t)((bitsPerDigit == 3) ? _digit8(c) : _digit16(c));
998
result = (result << bitsPerDigit) | dig;
1008
/* Convert C-style escapes in table */
1009
for (i=0; i<UNESCAPE_MAP_LENGTH; i+=2) {
1010
if (c == UNESCAPE_MAP[i]) {
1011
return UNESCAPE_MAP[i+1];
1012
} else if (c < UNESCAPE_MAP[i]) {
1017
/* If no special forms are recognized, then consider
1018
* the backslash to generically escape the next character.
1019
* Deal with surrogate pairs. */
1020
if (UTF_IS_FIRST_SURROGATE(c) && *offset < length) {
1021
UChar c2 = charAt(*offset, context);
1022
if (UTF_IS_SECOND_SURROGATE(c2)) {
1024
return UTF16_GET_PAIR_VALUE(c, c2);
1030
/* Invalid escape sequence */
1031
*offset = start; /* Reset to initial value */
1032
return (UChar32)0xFFFFFFFF;
1035
/* u_unescapeAt() callback to return a UChar from a char* */
1036
static UChar _charPtr_charAt(int32_t offset, void *context) {
1038
/* It would be more efficient to access the invariant tables
1039
* directly but there is no API for that. */
1040
u_charsToUChars(((char*) context) + offset, &c16, 1);
1044
/* Append an escape-free segment of the text; used by u_unescape() */
1045
static void _appendUChars(UChar *dest, int32_t destCapacity,
1046
const char *src, int32_t srcLen) {
1047
if (destCapacity < 0) {
1050
if (srcLen > destCapacity) {
1051
srcLen = destCapacity;
1053
u_charsToUChars(src, dest, srcLen);
1056
/* Do an invariant conversion of char* -> UChar*, with escape parsing */
1057
U_CAPI int32_t U_EXPORT2
1058
u_unescape(const char *src, UChar *dest, int32_t destCapacity) {
1059
const char *segment = src;
1063
while ((c=*src) != 0) {
1064
/* '\\' intentionally written as compiler-specific
1065
* character constant to correspond to compiler-specific
1066
* char* constants. */
1068
int32_t lenParsed = 0;
1070
if (src != segment) {
1072
_appendUChars(dest + i, destCapacity - i,
1073
segment, src - segment);
1077
++src; /* advance past '\\' */
1078
c32 = u_unescapeAt(_charPtr_charAt, &lenParsed, uprv_strlen(src), (void*)src);
1079
if (lenParsed == 0) {
1082
src += lenParsed; /* advance past escape seq. */
1083
if (dest != NULL && UTF_CHAR_LENGTH(c32) <= (destCapacity - i)) {
1084
UTF_APPEND_CHAR_UNSAFE(dest, i, c32);
1086
i += UTF_CHAR_LENGTH(c32);
1093
if (src != segment) {
1095
_appendUChars(dest + i, destCapacity - i,
1096
segment, src - segment);
1100
if (dest != NULL && i < destCapacity) {
1103
return i + 1; /* add 1 for zero term */
1106
if (dest != NULL && destCapacity > 0) {
1112
/* C UGrowBuffer implementation --------------------------------------------- */
1114
U_CAPI UBool /* U_CALLCONV U_EXPORT2 */
1115
u_growBufferFromStatic(void *context,
1116
UChar **pBuffer, int32_t *pCapacity, int32_t reqCapacity,
1118
UChar *newBuffer=(UChar *)uprv_malloc(reqCapacity*U_SIZEOF_UCHAR);
1119
if(newBuffer!=NULL) {
1121
uprv_memcpy(newBuffer, *pBuffer, length*U_SIZEOF_UCHAR);
1123
*pCapacity=reqCapacity;
1128
/* release the old pBuffer if it was not statically allocated */
1129
if(*pBuffer!=(UChar *)context) {
1130
uprv_free(*pBuffer);
1134
return (UBool)(newBuffer!=NULL);
1137
/* NUL-termination of strings ----------------------------------------------- */
1140
* NUL-terminate a string no matter what its type.
1141
* Set warning and error codes accordingly.
1143
#define __TERMINATE_STRING(dest, destCapacity, length, pErrorCode) \
1144
if(pErrorCode!=NULL && U_SUCCESS(*pErrorCode)) { \
1145
/* not a public function, so no complete argument checking */ \
1148
/* assume that the caller handles this */ \
1149
} else if(length<destCapacity) { \
1150
/* NUL-terminate the string, the NUL fits */ \
1152
/* unset the not-terminated warning but leave all others */ \
1153
if(*pErrorCode==U_STRING_NOT_TERMINATED_WARNING) { \
1154
*pErrorCode=U_ZERO_ERROR; \
1156
} else if(length==destCapacity) { \
1157
/* unable to NUL-terminate, but the string itself fit - set a warning code */ \
1158
*pErrorCode=U_STRING_NOT_TERMINATED_WARNING; \
1159
} else /* length>destCapacity */ { \
1160
/* even the string itself did not fit - set an error code */ \
1161
*pErrorCode=U_BUFFER_OVERFLOW_ERROR; \
1165
U_CAPI int32_t U_EXPORT2
1166
u_terminateUChars(UChar *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) {
1167
__TERMINATE_STRING(dest, destCapacity, length, pErrorCode);
1171
U_CAPI int32_t U_EXPORT2
1172
u_terminateChars(char *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) {
1173
__TERMINATE_STRING(dest, destCapacity, length, pErrorCode);
1177
U_CAPI int32_t U_EXPORT2
1178
u_terminateUChar32s(UChar32 *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) {
1179
__TERMINATE_STRING(dest, destCapacity, length, pErrorCode);
1183
U_CAPI int32_t U_EXPORT2
1184
u_terminateWChars(wchar_t *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) {
1185
__TERMINATE_STRING(dest, destCapacity, length, pErrorCode);