/*---------------------------------------------------------------------------* Project: TwlSDK - libraries - STD File: std_string.c Copyright 2005-2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2009-06-04#$ $Rev: 10698 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #include // Speed optimization macros for string operation functions // (Although these are platform-independent, components will overflow if they are enabled on ARM7, so they are enabled only on ARM9.) // #if defined(SDK_ARM9) // Because at least the high-speed version of GetStringLength does not consider big endians, just in case, implementation of all functions will be limited to little endians until their operation can be confirmed. // #if (PLATFORM_BYTES_ENDIAN == PLATFORM_ENDIAN_LITTLE) #define STRLEN_4BYTE_SKIP // 4-byte evaluation is for non-zero checks only #define STRLCPY_4BYTE // A copy evaluation every 4 bytes #define STRNLEN_4BYTE_SKIP // Measurement every 4 bytes #define STRCMP_4BYTE // Comparison every 4 bytes #define STRNCMP_4BYTE // Comparison every 4 bytes <- Disabled because of a bug //#define STRNICMP_DIFF // Improve the comparison #endif #endif /*---------------------------------------------------------------------------* Name: STD_CopyString Description: same to strcpy Arguments: destp : destination pointer srcp : src pointer Returns: pointer to destination *---------------------------------------------------------------------------*/ char *STD_CopyString(char *destp, const char *srcp) { char *retval = destp; SDK_ASSERT(destp && srcp); while (*srcp) { *destp++ = (char)*srcp++; } *destp = '\0'; return retval; } /*---------------------------------------------------------------------------* Name: STD_CopyLStringZeroFill Description: do not correspond with strlcpy Arguments: destp : destination pointer srcp : src pointer n: copy size + 1 Returns: size of src *---------------------------------------------------------------------------*/ int STD_CopyLStringZeroFill(char *destp, const char *srcp, int n) { int i; const char *s = srcp; SDK_ASSERT(destp && srcp && n >= 0); for (i = 0; i < n - 1; i++) { destp[i] = (char)*s; if (*s) { s++; } } if( i < n ) { destp[i] = '\0'; } return STD_GetStringLength(srcp); } /*---------------------------------------------------------------------------* Name: STD_CopyLString Description: same to strlcpy Arguments: destp : destination pointer srcp : src pointer siz : copy size + 1 Returns: size of src *---------------------------------------------------------------------------*/ int STD_CopyLString(char *destp, const char *srcp, int siz) { #if defined(STRLCPY_4BYTE) unsigned long dstalign, srcalign; int i; unsigned long val32, y; const char *s = srcp; SDK_ASSERT(destp && srcp && siz >= 0); // 4-byte alignment check dstalign = (unsigned long)destp & 0x3; srcalign = (unsigned long)srcp & 0x3; i = 0; // If it does not match, copy normally if( dstalign ^ srcalign ) { goto strlcpy_normal; } // Check copy of the leading 4 bytes if( dstalign ) { for( ; (i + dstalign < 4) && (i < siz - 1); i++ ) { *(destp + i) = (char)*s; if (*s) { s++; } else { goto terminate; } } } // Copy 4 bytes for( ; i < siz - 1 - 4 + 1; i += 4 ) { val32 = *(unsigned long*)(srcp + i); y = (val32 & 0x7F7F7F7F) + 0x7F7F7F7F; y = ~(y | val32 | 0x7F7F7F7F); if( y != 0 ) break; *(unsigned long*)(destp + i) = val32; } // Copy remaining bytes s = srcp + i; strlcpy_normal: // Copy byte for ( ; i < siz - 1; i++ ) { destp[i] = (char)*s; if (*s) { s++; } else { break; } } terminate: // Add terminating NULL if ((i >= siz - 1) && (siz > 0)) { destp[i] = '\0'; } if( !*s ) return (int)i; else return (int)i + STD_GetStringLength( s ); #else // original int i; const char *s = srcp; SDK_ASSERT(destp && srcp && siz >= 0); for (i = 0; i < siz - 1; i++) { destp[i] = (char)*s; if (*s) { s++; } else { break; } } if ((i >= siz - 1) && (siz > 0)) { destp[i] = '\0'; } return STD_GetStringLength(srcp); #endif } /*---------------------------------------------------------------------------* Name: STD_SearchChar Description: Same as strchr. Arguments: srcp : src string c: character to search from src pointer Returns: pointer to destination *---------------------------------------------------------------------------*/ char *STD_SearchChar(const char *srcp, int c) { SDK_POINTER_ASSERT(srcp); for (;;srcp++) { if (c == *srcp) { return (char*)srcp; } if ('\0' == *srcp) { return NULL; } } // not reach here. } /*---------------------------------------------------------------------------* Name: STD_SearchCharReverse Description: Same as strrchr. Arguments: srcp : src string c: character to search from src pointer Returns: pointer to destination *---------------------------------------------------------------------------*/ char *STD_SearchCharReverse(const char *srcp, int c) { const char* found = NULL; SDK_POINTER_ASSERT(srcp); for (;;srcp++) { if( c == *srcp ) { found = srcp; } if( '\0' == *srcp) { return (char*)found; } } // not reach here. } /*---------------------------------------------------------------------------* Name: STD_SearchString Description: same to strstr Arguments: srcp : src string str : string to search from src pointer Returns: pointer to destination *---------------------------------------------------------------------------*/ char *STD_SearchString(const char *srcp, const char *str) { int i; int n; SDK_ASSERT(srcp && str); for (i = 0; srcp[i]; i++) { n = 0; while (str[n] && srcp[i + n] == str[n]) { n++; } if (str[n] == '\0') { return (char *)&srcp[i]; } } return NULL; } /*---------------------------------------------------------------------------* Name: STD_GetStringLength Description: get string length. same to strlen Arguments: str : string Returns: string length *---------------------------------------------------------------------------*/ int STD_GetStringLength(const char *str) { #if defined(STRLEN_4BYTE_SKIP) int n; unsigned long *p32, y; for( n=0; ((unsigned long)(str + n) & 3) != 0; n++ ) { if(!str[n]) return n; } p32 = (unsigned long*)(str + n); for( ; ; n+=4 ) { y = (*p32 & 0x7F7F7F7F) + 0x7F7F7F7F; y = ~(y | *p32 | 0x7F7F7F7F); if( y != 0 ) break; p32++; } #if 1 while (str[n]) { n++; } return n; #else if( y & 0x000000FF ) return n; else if( y & 0x0000FF00 ) return n + 1; else if( y & 0x00FF0000 ) return n + 2; else return n + 3; // It is not possible to reach here return -1; #endif #else // original int n = 0; while (str[n]) { n++; } return n; #endif } /*---------------------------------------------------------------------------* Name: STD_GetStringNLength Description: Gets string length below len. Same as strnlen. Arguments: str : string len: max length Returns: string length *---------------------------------------------------------------------------*/ int STD_GetStringNLength(const char *str, int len) { #if defined(STRNLEN_4BYTE_SKIP) int n; unsigned long *p32, y; for( n=0; (((unsigned long)(str + n) & 3) != 0) && (n < len); n++ ) { if(!str[n]) return n; } p32 = (unsigned long*)(str + n); for( ; n < len - 4; n+=4 ) { y = (*p32 & 0x7F7F7F7F) + 0x7F7F7F7F; y = ~(y | *p32 | 0x7F7F7F7F); if( y != 0 ) break; p32++; } while( str[n] && (n < len) ) { n++; } return n; #else // original int n = 0; while( str[n] && (n < len) ) { n++; } return n; #endif } /*---------------------------------------------------------------------------* Name: STD_ConcatenateString Description: concatenate strings. same to strcat Arguments: str1 : original string str2 : string to concatenate Returns: concatenated string *---------------------------------------------------------------------------*/ char *STD_ConcatenateString(char *str1, const char *str2) { int n = STD_GetStringLength(str1); (void)STD_CopyString(&str1[n], str2); return str1; } /*---------------------------------------------------------------------------* Name: STD_ConcatenateLString Description: Concatenates strings. Same as strlcat. Arguments: str1 : original string str2 : string to concatenate size: buffer size of str1 Returns: length of str1 + length of str2 *---------------------------------------------------------------------------*/ int STD_ConcatenateLString(char *str1, const char *str2, int size) { int n; int str2_len; // If 'str1' is the length of 'size' or smaller and not terminated by NULL, [size + strlen(str2)] must be returned. for( n = 0; n < size; ++n ) { if( str1[n] == '\0' ) { break; } } if( n >= size ) { return n + STD_GetStringLength(str2); } str2_len = STD_CopyLString(&str1[n], str2, size - n); return n + str2_len; } /*---------------------------------------------------------------------------* Name: STD_CompareString Description: compare strings. same to strcmp Arguments: str1, str2 : strings Returns: 0 if same *---------------------------------------------------------------------------*/ int STD_CompareString(const char *str1, const char *str2) { #if defined(STRCMP_4BYTE) unsigned long dstalign, srcalign; unsigned char chr1, chr2; unsigned long lng1, lng2, y; int i; // 4-byte alignment check dstalign = (unsigned long)str1 & 0x3; srcalign = (unsigned long)str2 & 0x3; i = 0; // If it does not match, compare normally if( dstalign ^ srcalign ) { goto strcmp_normal; } // Compare the leading 4 bytes if( dstalign ) { for( ; i + dstalign < 4; i++ ) { chr1 = (unsigned char)*(str1 + i); chr2 = (unsigned char)*(str2 + i); if(chr1 != chr2 || !chr1) { return (int)(chr1 - chr2); } } } // Compare 4 bytes for( ; ; i += 4 ) { lng1 = *(unsigned long*)(str1 + i); lng2 = *(unsigned long*)(str2 + i); if( lng1 != lng2 ) break; y = (lng1 & 0x7F7F7F7F) + 0x7F7F7F7F; y = ~(y | lng1 | 0x7F7F7F7F); if( y != 0 ) break; } strcmp_normal: // Compare normally for(;;i++) { chr1 = (unsigned char)*(str1 + i); chr2 = (unsigned char)*(str2 + i); if(chr1 != chr2 || !chr1) { return (int)(chr1 - chr2); } } // This will not be reached return -1; #else // original while (*str1 == *str2 && *str1) { str1++; str2++; } return (*str1 - *str2); #endif } /*---------------------------------------------------------------------------* Name: STD_CompareNString Description: same to strncmp Arguments: str1, str2 : strings len: max length Returns: 0 if same *---------------------------------------------------------------------------*/ int STD_CompareNString(const char *str1, const char *str2, int len) { #if defined(STRNCMP_4BYTE) unsigned long dstalign, srcalign; unsigned char chr1, chr2; unsigned long lng1, lng2; unsigned long y; int i; if( len == 0 ) goto end; // 4-byte alignment check dstalign = (unsigned long)str1 & 0x3; srcalign = (unsigned long)str2 & 0x3; i = 0; // If it does not match, compare normally if( dstalign ^ srcalign ) { goto strncmp_normal; } // Compare the leading 4 bytes if( dstalign ) { for( ; (i + dstalign < 4) && (i < len); i++ ) { chr1 = (unsigned char)*(str1 + i); chr2 = (unsigned char)*(str2 + i); if(chr1 != chr2 || !chr1) { return (int)(chr1 - chr2); } } } // Compare 4 bytes for( ; i <= len - 4; i += 4 ) { lng1 = *(unsigned long*)(str1 + i); lng2 = *(unsigned long*)(str2 + i); // Check comparison if( lng1 != lng2 ) break; // NULL check y = (lng1 & 0x7F7F7F7F) + 0x7F7F7F7F; y = ~(y | lng1 | 0x7F7F7F7F); if( y != 0 ) break; } strncmp_normal: // Compare normally for (; i < len; ++i) { chr1 = (unsigned char)*(str1 + i); chr2 = (unsigned char)*(str2 + i); if(chr1 != chr2 || !chr1) { return (int)(chr1 - chr2); } } end: // Return value return 0; #else if (len) { int i; for (i = 0; i < len; ++i) { int c = (int)(MI_ReadByte(str1 + i)); int d = (int)(MI_ReadByte(str2 + i)); if (c != d || !c) { return (int)(c - d); } } } return 0; #endif } /*---------------------------------------------------------------------------* Name: STD_CompareLString Description: same to strlcmp Arguments: str1, str2 : strings len: max length Returns: 0 if same *---------------------------------------------------------------------------*/ int STD_CompareLString(const char *str1, const char *str2, int len) { int c, d; while(len-- && *str2 != '\0') { c = (int)(MI_ReadByte(str1)); d = (int)(MI_ReadByte(str2)); if( c != d ) { return c - d; } str1++; str2++; } return (int)(MI_ReadByte(str1)); } /*---------------------------------------------------------------------------* Name: STD_CompareNString Description: Same as strncasecmp. Arguments: str1, str2 : strings len: max length Returns: 0 if same *---------------------------------------------------------------------------*/ int STD_CompareNIString(const char *str1, const char *str2, int len) { #if defined(STRNICMP_DIFF) int i; char c, d, diff; int retval = 0; #define TO_UPPER(x) ( ((x >= 'a') && (x <= 'z')) ? (x - 0x20) : x ) if( (str1 == str2) || (len == 0) ) { return 0; } for( i = 0; ; i++ ) { c = *(str1 + i); d = *(str2 + i); diff = (char)(c - d); if( diff == 0 ) { goto check_end; } else { if( diff == 'a' - 'A' ) { if( (d >= 'A') && (d <= 'Z') ) { goto check_end; } } else if( diff == 'A' - 'a' ) { if( (c >= 'A') && (c <= 'Z') ) { goto check_end; } } retval = TO_UPPER(c) - TO_UPPER(d); break; } check_end: if( c == '\0' ) break; if( i >= len - 1 ) break; } return retval; #else int retval = 0; #define TO_UPPER(x) ( ((x >= 'a') && (x <= 'z')) ? (x - 0x20) : x ) if( (str1 == str2) || (len == 0) ) { return 0; } while( !(retval = TO_UPPER(*str1) - TO_UPPER(*str2)) ) { if( (*str1 == '\0') || (--len == 0) ) { break; } str1++; str2++; } return retval; #endif } /*---------------------------------------------------------------------------* Name: STD_TSScanf Description: This is sscanf with size reduced. Supports basic format specifications "%(*?)([lh]{,2})([diouxXpn])". Arguments: src: Input string fmt: Format control string Returns: Total number of substituted values. Returns -1 if the function ends without a substitution or an error is detected. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL int STD_TSScanf(const char *src, const char *fmt, ...) { int ret; va_list va; va_start(va, fmt); ret = STD_TVSScanf(src, fmt, va); va_end(va); return ret; } /*---------------------------------------------------------------------------* Name: STDi_IsSpace Description: Determines if the specified character is a whitespace character. Arguments: c: Character to evaluate. Returns: TRUE if whitespace. *---------------------------------------------------------------------------*/ static BOOL STDi_IsSpace(int c) { return (c == '\f') || (c == '\n') || (c == '\r') || (c == '\t') || (c == '\v') || (c == ' '); } /*---------------------------------------------------------------------------* Name: STDi_GetBitset Description: Gets the contents of the specified position of the bit set Arguments: bitet: Bitset array. i: Position of the information to be gotten. Returns: TRUE if ON, FALSE if OFF *---------------------------------------------------------------------------*/ inline static u32 STDi_GetBitset(const u32 *bitset, u32 i) { const u32 pos = (u32)(i >> 5UL); const u32 bit = (u32)(i & 31UL); return ((bitset[pos] >> bit) & 1UL); } /*---------------------------------------------------------------------------* Name: STDi_SetBitset Description: Sets the specified position of the bitset to ON. Arguments: bitet: Bitset array. i: Position to be turned ON Returns: None. *---------------------------------------------------------------------------*/ inline static void STDi_SetBitset(u32 *bitset, u32 i) { const u32 pos = (i >> 5UL); const u32 bit = (i & 31UL); bitset[pos] |= (1UL << bit); } /*---------------------------------------------------------------------------* Name: STDi_FillBitset Description: Sets the specified range [a,b) of the bitset to ON. Arguments: bitet: Bitset array. a: Start position b: End position Returns: None. *---------------------------------------------------------------------------*/ static void STDi_FillBitset(u32 *bitset, u32 a, u32 b) { while (a < b) { const u32 pos = (u32)(a >> 5UL); const u32 bit = (u32)(a & 31UL); u32 mask = (~0UL << bit); a = (pos + 1UL) * 32UL; if (a > b) { mask &= (u32)((1UL << (b & 31UL)) - 1UL); } bitset[pos] |= mask; } } /*---------------------------------------------------------------------------* Name: STD_TVSScanf Description: This version supports va_list used with STD_TSScanf. Supports basic format specifications. Arguments: src: Input string fmt: Format control string vlist: Parameters Returns: Total number of substituted values. Returns -1 if the function ends without a substitution or an error is detected. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL int STD_TVSScanf(const char *src, const char *fmt, va_list vlist) { /* Start of verification string (required for %n) */ const char *src_base = src; /* Has the format been matched even once? (return -1 if not) */ BOOL matched = FALSE; /* Number of substitutions made. (return >=0 in this case) */ int stored = 0; /* Parse format string one character at a time */ while (*fmt) { char c = *fmt; /* Skip both formatting/verification if whitespace */ if (STDi_IsSpace(c)) { while (STDi_IsSpace(*fmt)) { ++fmt; } while (STDi_IsSpace(*src)) { ++src; } continue; } /* Conventional characters must match exactly */ else if (c != '%') { if (c != *src) { break; } /* Check next byte if SHIFT-JIS */ if ((unsigned int)(((unsigned char)c ^ 0x20) - 0xA1) < 0x3C) { if (c != *++src) { break; } } ++src; ++fmt; } /* Simply compare if the escape character '%' is detected */ else if (fmt[1] == '%') { if (c != *src) { break; } fmt += 2; } /* Start format analysis if this is a conversion specification */ else { enum { flag_plus = 000002, /* '+' */ flag_minus = 000010, /* '-' */ flag_l1 = 000040, /* "l" */ flag_h1 = 000100, /* "h" */ flag_l2 = 000200, /* "ll" */ flag_h2 = 000400, /* "hh" */ flag_unsigned = 010000, /* 'o', 'u', ... */ flag_ignored = 020000, /* '*' */ flag_end }; u64 val = 0; int flag = 0, width = 0, radix = 10; int digit; /* Prohibit substitution flag */ c = *++fmt; if (c == '*') { flag |= flag_ignored; c = *++fmt; } /* Specify width */ while ((c >= '0') && (c <= '9')) { width = (width * 10) + c - '0'; c = *++fmt; } /* Modify conversion */ switch (c) { case 'h': c = *++fmt; if (c != 'h') { flag |= flag_h1; } else flag |= flag_h2, c = *++fmt; break; case 'l': c = *++fmt; if (c != 'l') flag |= flag_l1; else flag |= flag_l2, c = *++fmt; break; } /* Conversion type */ switch (c) { case 'd': /* Signed decimal number */ radix = 10; goto get_integer; case 'i': /* Signed base-8/10/16 number */ radix = -1; goto get_integer; case 'o': /* Signed octal number */ radix = 8; goto get_integer; case 'u': /* Unsigned decimal number */ radix = 10; flag |= flag_unsigned; goto get_integer; case 'X': /* Unsigned hexadecimal number */ case 'x': /* Unsigned hexadecimal number */ case 'p': /* Pointer conversion (unsigned hexadecimal) */ radix = 16; flag |= flag_unsigned; goto get_integer; case 's': /* Character string up to non-whitespace */ case 'c': /* Character string of specified width only */ goto store_string; case '[': /* Character class */ goto store_char_class; case 'n': /* Store match location */ /* Since this doesn't contribute to the substitution count, subtract it here. */ if (!(flag & flag_ignored)) { --stored; } val = src - src_base; c = *++fmt; goto store_integer; } // invalid: /* Parsing ended in an unfortunate result */ break; get_integer: /* Integer input processing */ ++fmt; c = *src; /* Skip whitespace */ while (STDi_IsSpace(c)) { c = *++src; } /* Get sign */ for (;; c = *++src) { if (c == '+') { flag |= flag_plus; } else if (c == '-') { /* '-' in an unsigned type is an error */ if ((flag & flag_unsigned) != 0) { /* * ...would be a good way to do things, but since this is ignored with gcc and CW, the SDK currently follows their lead. */ //goto invalid; } flag |= flag_minus; } else { break; } } /* Automatic detection of integers (%i) */ if (radix == -1) { if (c != '0') { radix = 10; } else if ((src[1] == 'x') || (src[1] == 'X')) { radix = 16; } else { radix = 8; } } /* If hexadecimal, skip "^(0[xX])?" */ if ((radix == 16) && (c == '0') && ((src[1] == 'x') || (src[1] == 'X'))) { src += 2; c = *src; } /* Get integer */ if (width == 0) { width = 0x7FFFFFFF; } for (digit = 0; digit < width; ++digit) { u32 d = (u32)(c - '0'); if (d >= 10) { d -= (u32)('a' - '0'); if (d < 6) { d += 10; } else { d -= (u32)('A' - 'a'); if (d < 6) { d += 10; } } } if (d >= radix) { break; } c = *++src; val = val * radix + d; } /* No input is an error */ if (digit == 0) { break; } /* Adjust sign */ if (flag & flag_minus) { val = (u64)(val * -1); } /* At least conversion succeeded */ matched = TRUE; store_integer: /* Substitute */ if (!(flag & flag_ignored)) { ++stored; if (flag & flag_h2) { *va_arg(vlist, u8 *) = (u8)val; } else if (flag & flag_h1) { *va_arg(vlist, u16 *) = (u16)val; } else if (flag & flag_l2) { *va_arg(vlist, u64 *) = (u64)val; } else { *va_arg(vlist, u32 *) = (u32)val; } } continue; store_string: /* Substitute character string */ { char *dst = NULL, *dst_bak = NULL; ++fmt; if (!(flag & flag_ignored)) { dst = va_arg(vlist, char *); dst_bak = dst; } /* * If s, search until you find whitespace and append '\0' * If c, search for the specified width */ if (c == 's') { if (width == 0) { width = 0x7FFFFFFF; } for (c = *src; STDi_IsSpace(c); c = *++src) { } for (; c && !STDi_IsSpace(c) && (width > 0); --width, c = *++src) { if (dst) { *dst++ = c; } } /* * If [dst! = dst_bak], it is assumed that more than one character was substituted * Append NULL to the end, make [matched = TRUE], and ++ the substitute success number */ if (dst != dst_bak) { *dst++ = '\0'; ++stored; matched = TRUE; } } else { if (width == 0) { width = 1; } for (c = *src; c && (width > 0); --width, c = *++src) { if (dst) { *dst++ = c; } } /* * If [dst! = dst_bak], it is assumed that more than one character was substituted * Append NULL to the end, make [matched = TRUE], and ++ the substitute success number */ if (dst != dst_bak) { ++stored; matched = TRUE; if(width > 0) { *dst++ = '\0'; } } } } continue; store_char_class: ++fmt; /* Character class substitution processing */ { char *dst = NULL; u32 bitset[256 / (8 * sizeof(u32))]; u32 matchcond = 1; u32 from = 0; BOOL in_range = FALSE; MI_CpuFill32(bitset, 0x00000000UL, sizeof(bitset)); if (*fmt == '^') { matchcond = 0; ++fmt; } /* Start-of-line escape */ if (*fmt == ']') { STDi_SetBitset(bitset, (u8)*fmt); ++fmt; } /* Character class analysis */ for (;; ++fmt) { /* Terminus detection */ if (!*fmt || (*fmt == ']')) { /* A terminus whose range is being specified is treated as a simple character */ if (in_range) { STDi_SetBitset(bitset, from); STDi_SetBitset(bitset, (u32)'-'); } if (*fmt == ']') { ++fmt; } break; } /* Beginning of the simple character or specified range */ else if (!in_range) { /* Begin range specification */ if ((from != 0) && (*fmt == '-')) { in_range = TRUE; } else { STDi_SetBitset(bitset, (u8)*fmt); from = (u8)*fmt; } } /* End range specification */ else { u32 to = (u8)*fmt; /* Illegal specified ranges are treated as separate specified characters */ if (from > to) { STDi_SetBitset(bitset, from); STDi_SetBitset(bitset, (u32)'-'); STDi_SetBitset(bitset, to); } /* Also includes and collectively sets terminal characters */ else { STDi_FillBitset(bitset, from, to + 1UL); } in_range = FALSE; from = 0; } } /* Verification of character class and string */ /* At least conversion has been successful to this point */ matched = TRUE; if (!(flag & flag_ignored)) { ++stored; dst = va_arg(vlist, char *); } if (width == 0) { width = 0x7FFFFFFF; } for (c = *src; c && (width > 0); --width, c = *++src) { if (STDi_GetBitset(bitset, (u8)c) != matchcond) { break; } if (dst) { *dst++ = c; } } if (dst) { *dst++ = '\0'; } } continue; } } return (*src || matched) ? stored : -1; }