Merge "Support %mc/%ms/%m[ in sscanf."
This commit is contained in:
commit
2b66844a0b
3 changed files with 307 additions and 113 deletions
|
@ -18,7 +18,7 @@ libc_common_src_files = [
|
|||
"stdio/refill.c",
|
||||
"stdio/stdio.cpp",
|
||||
"stdio/stdio_ext.cpp",
|
||||
"stdio/vfscanf.c",
|
||||
"stdio/vfscanf.cpp",
|
||||
"stdio/vfwscanf.c",
|
||||
"stdlib/exit.c",
|
||||
]
|
||||
|
|
|
@ -37,74 +37,68 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <wctype.h>
|
||||
#include "local.h"
|
||||
|
||||
#include <private/bionic_ctype.h>
|
||||
#include <private/bionic_fortify.h>
|
||||
#include <private/bionic_mbstate.h>
|
||||
|
||||
#define BUF 513 /* Maximum length of numeric string. */
|
||||
|
||||
/*
|
||||
* Flags used during conversion.
|
||||
*/
|
||||
#define LONG 0x00001 /* l: long or double */
|
||||
#define LONGDBL 0x00002 /* L: long double */
|
||||
#define SHORT 0x00004 /* h: short */
|
||||
#define SHORTSHORT 0x00008 /* hh: 8 bit integer */
|
||||
#define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */
|
||||
#define POINTER 0x00020 /* p: void * (as hex) */
|
||||
#define SIZEINT 0x00040 /* z: (signed) size_t */
|
||||
#define MAXINT 0x00080 /* j: intmax_t */
|
||||
#define PTRINT 0x00100 /* t: ptrdiff_t */
|
||||
#define NOSKIP 0x00200 /* [ or c: do not skip blanks */
|
||||
#define SUPPRESS 0x00400 /* *: suppress assignment */
|
||||
#define UNSIGNED 0x00800 /* %[oupxX] conversions */
|
||||
// Flags used during conversion.
|
||||
// Size/type:
|
||||
#define LONG 0x00001 // l: long or double
|
||||
#define LONGDBL 0x00002 // L: long double
|
||||
#define SHORT 0x00004 // h: short
|
||||
#define SHORTSHORT 0x00008 // hh: 8 bit integer
|
||||
#define LLONG 0x00010 // ll: long long (+ deprecated q: quad)
|
||||
#define POINTER 0x00020 // p: void* (as hex)
|
||||
#define SIZEINT 0x00040 // z: (signed) size_t
|
||||
#define MAXINT 0x00080 // j: intmax_t
|
||||
#define PTRINT 0x00100 // t: ptrdiff_t
|
||||
#define NOSKIP 0x00200 // [ or c: do not skip blanks
|
||||
// Modifiers:
|
||||
#define SUPPRESS 0x00400 // *: suppress assignment
|
||||
#define UNSIGNED 0x00800 // %[oupxX] conversions
|
||||
#define ALLOCATE 0x01000 // m: allocate a char*
|
||||
// Internal use during integer parsing:
|
||||
#define SIGNOK 0x02000 // +/- is (still) legal
|
||||
#define HAVESIGN 0x04000 // Sign detected
|
||||
#define NDIGITS 0x08000 // No digits detected
|
||||
#define PFXOK 0x10000 // "0x" prefix is (still) legal
|
||||
#define NZDIGITS 0x20000 // No zero digits detected
|
||||
|
||||
/*
|
||||
* The following are used in numeric conversions only:
|
||||
* SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
|
||||
* SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
|
||||
*/
|
||||
#define SIGNOK 0x01000 /* +/- is (still) legal */
|
||||
#define HAVESIGN 0x02000 /* sign detected */
|
||||
#define NDIGITS 0x04000 /* no digits detected */
|
||||
// Conversion types.
|
||||
#define CT_CHAR 0 // %c conversion
|
||||
#define CT_CCL 1 // %[...] conversion
|
||||
#define CT_STRING 2 // %s conversion
|
||||
#define CT_INT 3 // Integer: strtoimax/strtoumax
|
||||
#define CT_FLOAT 4 // Float: strtod
|
||||
|
||||
#define DPTOK 0x08000 /* (float) decimal point is still legal */
|
||||
#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
|
||||
|
||||
#define PFXOK 0x08000 /* 0x prefix is (still) legal */
|
||||
#define NZDIGITS 0x10000 /* no zero digits detected */
|
||||
|
||||
/*
|
||||
* Conversion types.
|
||||
*/
|
||||
#define CT_CHAR 0 /* %c conversion */
|
||||
#define CT_CCL 1 /* %[...] conversion */
|
||||
#define CT_STRING 2 /* %s conversion */
|
||||
#define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */
|
||||
#define CT_FLOAT 4 /* floating, i.e., strtod */
|
||||
|
||||
static u_char* __sccl(char*, u_char*);
|
||||
static const unsigned char* __sccl(char*, const unsigned char*);
|
||||
|
||||
/*
|
||||
* Internal, unlocked version of vfscanf
|
||||
*/
|
||||
int __svfscanf(FILE* fp, const char* fmt0, __va_list ap) {
|
||||
u_char* fmt = (u_char*)fmt0;
|
||||
int __svfscanf(FILE* fp, const char* fmt0, va_list ap) {
|
||||
const unsigned char* fmt = reinterpret_cast<const unsigned char*>(fmt0);
|
||||
int c; /* character from format, or conversion */
|
||||
size_t width; /* field width, or 0 */
|
||||
char* p; /* points into all kinds of strings */
|
||||
int n; /* handy integer */
|
||||
char* p;
|
||||
wchar_t* wcp;
|
||||
size_t n;
|
||||
int flags; /* flags as defined above */
|
||||
char* p0; /* saves original value of p when necessary */
|
||||
int nassigned; /* number of fields assigned */
|
||||
int nread; /* number of characters consumed from fp */
|
||||
int base; /* base argument to strtoimax/strtouimax */
|
||||
char ccltab[256]; /* character class table for %[...] */
|
||||
char buf[BUF]; /* buffer for numeric conversions */
|
||||
wchar_t* wcp; /* handy wide character pointer */
|
||||
size_t nconv; /* length of multibyte sequence converted */
|
||||
mbstate_t mbs;
|
||||
void* allocation = NULL; // Allocated but unassigned result for %mc/%ms/%m[.
|
||||
size_t capacity = 0; // Number of char/wchar_t units allocated in `allocation`.
|
||||
|
||||
/* `basefix' is used to avoid `if' tests in the integer scanner */
|
||||
static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
|
||||
|
@ -113,10 +107,9 @@ int __svfscanf(FILE* fp, const char* fmt0, __va_list ap) {
|
|||
|
||||
nassigned = 0;
|
||||
nread = 0;
|
||||
base = 0; /* XXX just to keep gcc happy */
|
||||
for (;;) {
|
||||
c = *fmt++;
|
||||
if (c == 0) return (nassigned);
|
||||
if (c == 0) return nassigned;
|
||||
if (IsSpace(c)) {
|
||||
while ((fp->_r > 0 || __srefill(fp) == 0) && IsSpace(*fp->_p)) nread++, fp->_r--, fp->_p++;
|
||||
continue;
|
||||
|
@ -164,6 +157,9 @@ literal:
|
|||
flags |= LONG;
|
||||
}
|
||||
goto again;
|
||||
case 'm':
|
||||
flags |= ALLOCATE;
|
||||
goto again;
|
||||
case 'q':
|
||||
flags |= LLONG; /* deprecated */
|
||||
goto again;
|
||||
|
@ -262,29 +258,30 @@ literal:
|
|||
|
||||
case 'n':
|
||||
if (flags & SUPPRESS) continue;
|
||||
if (flags & SHORTSHORT)
|
||||
if (flags & SHORTSHORT) {
|
||||
*va_arg(ap, signed char*) = nread;
|
||||
else if (flags & SHORT)
|
||||
} else if (flags & SHORT) {
|
||||
*va_arg(ap, short*) = nread;
|
||||
else if (flags & LONG)
|
||||
} else if (flags & LONG) {
|
||||
*va_arg(ap, long*) = nread;
|
||||
else if (flags & SIZEINT)
|
||||
} else if (flags & SIZEINT) {
|
||||
*va_arg(ap, ssize_t*) = nread;
|
||||
else if (flags & PTRINT)
|
||||
} else if (flags & PTRINT) {
|
||||
*va_arg(ap, ptrdiff_t*) = nread;
|
||||
else if (flags & LLONG)
|
||||
} else if (flags & LLONG) {
|
||||
*va_arg(ap, long long*) = nread;
|
||||
else if (flags & MAXINT)
|
||||
} else if (flags & MAXINT) {
|
||||
*va_arg(ap, intmax_t*) = nread;
|
||||
else
|
||||
} else {
|
||||
*va_arg(ap, int*) = nread;
|
||||
}
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Disgusting backwards compatibility hacks. XXX
|
||||
*/
|
||||
case '\0': /* compat */
|
||||
return (EOF);
|
||||
return EOF;
|
||||
|
||||
default: /* compat */
|
||||
if (IsUpper(c)) flags |= LONG;
|
||||
|
@ -293,6 +290,13 @@ literal:
|
|||
break;
|
||||
}
|
||||
|
||||
if ((flags & ALLOCATE) != 0 && c > CT_STRING) {
|
||||
__fortify_fatal("scanf 'm' only works with %%c/%%s/%%[");
|
||||
}
|
||||
if ((flags & (ALLOCATE|SUPPRESS)) == (ALLOCATE|SUPPRESS)) {
|
||||
__fortify_fatal("scanf 'm' makes no sense with '*'");
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a conversion that requires input.
|
||||
*/
|
||||
|
@ -326,42 +330,53 @@ literal:
|
|||
/* scan arbitrary characters (sets NOSKIP) */
|
||||
if (width == 0) width = 1;
|
||||
if (flags & LONG) {
|
||||
wcp = ((flags & SUPPRESS) == 0) ? va_arg(ap, wchar_t*) : NULL;
|
||||
n = 0;
|
||||
if (flags & ALLOCATE) {
|
||||
allocation = wcp = reinterpret_cast<wchar_t*>(malloc(width * sizeof(wchar_t)));
|
||||
if (allocation == NULL) goto allocation_failure;
|
||||
} else if (flags & SUPPRESS) {
|
||||
wcp = NULL;
|
||||
} else {
|
||||
wcp = va_arg(ap, wchar_t*);
|
||||
}
|
||||
size_t bytes = 0;
|
||||
while (width != 0) {
|
||||
if (n == (int)MB_CUR_MAX) {
|
||||
if (bytes == MB_CUR_MAX) {
|
||||
fp->_flags |= __SERR;
|
||||
goto input_failure;
|
||||
}
|
||||
buf[n++] = *fp->_p;
|
||||
buf[bytes++] = *fp->_p;
|
||||
fp->_p++;
|
||||
fp->_r--;
|
||||
memset(&mbs, 0, sizeof(mbs));
|
||||
nconv = mbrtowc(wcp, buf, n, &mbs);
|
||||
if (nconv == (size_t)-1) {
|
||||
nconv = mbrtowc(wcp, buf, bytes, &mbs);
|
||||
if (nconv == __MB_ERR_ILLEGAL_SEQUENCE) {
|
||||
fp->_flags |= __SERR;
|
||||
goto input_failure;
|
||||
}
|
||||
if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0';
|
||||
if (nconv != (size_t)-2) {
|
||||
nread += n;
|
||||
if (nconv != __MB_ERR_INCOMPLETE_SEQUENCE) {
|
||||
nread += bytes;
|
||||
width--;
|
||||
if (!(flags & SUPPRESS)) wcp++;
|
||||
n = 0;
|
||||
bytes = 0;
|
||||
}
|
||||
if (fp->_r <= 0 && __srefill(fp)) {
|
||||
if (n != 0) {
|
||||
if (bytes != 0) {
|
||||
fp->_flags |= __SERR;
|
||||
goto input_failure;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allocation != NULL) {
|
||||
*va_arg(ap, wchar_t**) = reinterpret_cast<wchar_t*>(allocation);
|
||||
allocation = NULL;
|
||||
}
|
||||
if (!(flags & SUPPRESS)) nassigned++;
|
||||
} else if (flags & SUPPRESS) {
|
||||
size_t sum = 0;
|
||||
for (;;) {
|
||||
if ((n = fp->_r) < (int)width) {
|
||||
if ((n = fp->_r) < width) {
|
||||
sum += n;
|
||||
width -= n;
|
||||
fp->_p += n;
|
||||
|
@ -378,9 +393,18 @@ literal:
|
|||
}
|
||||
nread += sum;
|
||||
} else {
|
||||
size_t r = fread((void*)va_arg(ap, char*), 1, width, fp);
|
||||
|
||||
if (flags & ALLOCATE) {
|
||||
allocation = p = reinterpret_cast<char*>(malloc(width));
|
||||
if (allocation == NULL) goto allocation_failure;
|
||||
} else {
|
||||
p = va_arg(ap, char*);
|
||||
}
|
||||
size_t r = fread(p, 1, width, fp);
|
||||
if (r == 0) goto input_failure;
|
||||
if (allocation != NULL) {
|
||||
*va_arg(ap, char**) = reinterpret_cast<char*>(allocation);
|
||||
allocation = NULL;
|
||||
}
|
||||
nread += r;
|
||||
nassigned++;
|
||||
}
|
||||
|
@ -390,55 +414,72 @@ literal:
|
|||
case CT_STRING:
|
||||
// CT_CCL: scan a (nonempty) character class (sets NOSKIP).
|
||||
// CT_STRING: like CCL, but zero-length string OK, & no NOSKIP.
|
||||
if (width == 0) width = (size_t)~0; // 'infinity'.
|
||||
if (width == 0) width = SIZE_MAX;
|
||||
if (flags & LONG) {
|
||||
wchar_t twc;
|
||||
int nchars = 0;
|
||||
|
||||
wcp = (flags & SUPPRESS) == 0 ? va_arg(ap, wchar_t*) : &twc;
|
||||
// TODO: since no-one cares, replace this with a simple fgetwc loop?
|
||||
n = 0;
|
||||
if (flags & ALLOCATE) {
|
||||
capacity = MIN(width, 32);
|
||||
allocation = wcp = reinterpret_cast<wchar_t*>(malloc(sizeof(wchar_t) * capacity));
|
||||
if (allocation == NULL) goto allocation_failure;
|
||||
} else if (flags & SUPPRESS) {
|
||||
wcp = NULL;
|
||||
} else {
|
||||
wcp = va_arg(ap, wchar_t*);
|
||||
}
|
||||
size_t bytes = 0;
|
||||
while ((c == CT_CCL || !IsSpace(*fp->_p)) && width != 0) {
|
||||
if (n == (int)MB_CUR_MAX) {
|
||||
if (bytes == MB_CUR_MAX) {
|
||||
fp->_flags |= __SERR;
|
||||
goto input_failure;
|
||||
}
|
||||
buf[n++] = *fp->_p;
|
||||
buf[bytes++] = *fp->_p;
|
||||
fp->_p++;
|
||||
fp->_r--;
|
||||
wchar_t wc = L'\0';
|
||||
memset(&mbs, 0, sizeof(mbs));
|
||||
nconv = mbrtowc(wcp, buf, n, &mbs);
|
||||
if (nconv == (size_t)-1) {
|
||||
nconv = mbrtowc(&wc, buf, bytes, &mbs);
|
||||
if (nconv == __MB_ERR_ILLEGAL_SEQUENCE) {
|
||||
fp->_flags |= __SERR;
|
||||
goto input_failure;
|
||||
}
|
||||
if (nconv == 0) *wcp = L'\0';
|
||||
if (nconv != (size_t)-2) {
|
||||
if ((c == CT_CCL && wctob(*wcp) != EOF && !ccltab[wctob(*wcp)]) || (c == CT_STRING && iswspace(*wcp))) {
|
||||
while (n != 0) {
|
||||
n--;
|
||||
ungetc(buf[n], fp);
|
||||
if (nconv != __MB_ERR_INCOMPLETE_SEQUENCE) {
|
||||
if ((c == CT_CCL && wctob(wc) != EOF && !ccltab[wctob(wc)]) || (c == CT_STRING && iswspace(wc))) {
|
||||
while (bytes != 0) {
|
||||
bytes--;
|
||||
ungetc(buf[bytes], fp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
nread += n;
|
||||
if (wcp) wcp[n] = wc;
|
||||
n++;
|
||||
if (allocation != NULL && n == capacity) {
|
||||
capacity *= 2;
|
||||
wchar_t* new_allocation =
|
||||
reinterpret_cast<wchar_t*>(realloc(allocation, sizeof(wchar_t) * capacity));
|
||||
if (new_allocation == NULL) goto allocation_failure;
|
||||
allocation = wcp = new_allocation;
|
||||
}
|
||||
nread += bytes;
|
||||
width--;
|
||||
if (!(flags & SUPPRESS)) wcp++;
|
||||
nchars++;
|
||||
n = 0;
|
||||
bytes = 0;
|
||||
}
|
||||
if (fp->_r <= 0 && __srefill(fp)) {
|
||||
if (n != 0) {
|
||||
if (bytes != 0) {
|
||||
fp->_flags |= __SERR;
|
||||
goto input_failure;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == CT_CCL && n != 0) {
|
||||
if (c == CT_CCL && bytes != 0) {
|
||||
fp->_flags |= __SERR;
|
||||
goto input_failure;
|
||||
}
|
||||
n = nchars;
|
||||
if (allocation != NULL) {
|
||||
*va_arg(ap, wchar_t**) = reinterpret_cast<wchar_t*>(allocation);
|
||||
allocation = NULL;
|
||||
}
|
||||
} else if (flags & SUPPRESS) {
|
||||
n = 0;
|
||||
while ((c == CT_CCL && ccltab[*fp->_p]) || (c == CT_STRING && !IsSpace(*fp->_p))) {
|
||||
|
@ -449,29 +490,46 @@ literal:
|
|||
break;
|
||||
}
|
||||
}
|
||||
nread += n;
|
||||
} else {
|
||||
p0 = p = va_arg(ap, char*);
|
||||
if (flags & ALLOCATE) {
|
||||
capacity = MIN(width, 32);
|
||||
allocation = p = reinterpret_cast<char*>(malloc(capacity));
|
||||
if (allocation == NULL) goto allocation_failure;
|
||||
} else {
|
||||
p = va_arg(ap, char*);
|
||||
}
|
||||
n = 0;
|
||||
while ((c == CT_CCL && ccltab[*fp->_p]) || (c == CT_STRING && !IsSpace(*fp->_p))) {
|
||||
fp->_r--;
|
||||
*p++ = *fp->_p++;
|
||||
p[n++] = *fp->_p++;
|
||||
if (allocation != NULL && n == capacity) {
|
||||
capacity *= 2;
|
||||
char* new_allocation = reinterpret_cast<char*>(realloc(allocation, capacity));
|
||||
if (new_allocation == NULL) goto allocation_failure;
|
||||
allocation = p = new_allocation;
|
||||
}
|
||||
if (--width == 0) break;
|
||||
if (fp->_r <= 0 && __srefill(fp)) {
|
||||
if (c == CT_CCL && p == p0) goto input_failure;
|
||||
if (c == CT_CCL && n == 0) goto input_failure;
|
||||
break;
|
||||
}
|
||||
}
|
||||
n = p - p0;
|
||||
nread += n;
|
||||
if (allocation != NULL) {
|
||||
*va_arg(ap, char**) = reinterpret_cast<char*>(allocation);
|
||||
allocation = NULL;
|
||||
}
|
||||
}
|
||||
if (c == CT_CCL && n == 0) goto match_failure;
|
||||
if (!(flags & SUPPRESS)) {
|
||||
if (flags & LONG) {
|
||||
*wcp = L'\0';
|
||||
wcp[n] = L'\0';
|
||||
} else {
|
||||
*p = '\0';
|
||||
p[n] = '\0';
|
||||
}
|
||||
++nassigned;
|
||||
}
|
||||
nread += n;
|
||||
break;
|
||||
|
||||
case CT_INT:
|
||||
|
@ -610,28 +668,30 @@ literal:
|
|||
uintmax_t res;
|
||||
|
||||
*p = '\0';
|
||||
if (flags & UNSIGNED)
|
||||
if (flags & UNSIGNED) {
|
||||
res = strtoumax(buf, NULL, base);
|
||||
else
|
||||
} else {
|
||||
res = strtoimax(buf, NULL, base);
|
||||
if (flags & POINTER)
|
||||
}
|
||||
if (flags & POINTER) {
|
||||
*va_arg(ap, void**) = (void*)(uintptr_t)res;
|
||||
else if (flags & MAXINT)
|
||||
} else if (flags & MAXINT) {
|
||||
*va_arg(ap, intmax_t*) = res;
|
||||
else if (flags & LLONG)
|
||||
} else if (flags & LLONG) {
|
||||
*va_arg(ap, long long*) = res;
|
||||
else if (flags & SIZEINT)
|
||||
} else if (flags & SIZEINT) {
|
||||
*va_arg(ap, ssize_t*) = res;
|
||||
else if (flags & PTRINT)
|
||||
} else if (flags & PTRINT) {
|
||||
*va_arg(ap, ptrdiff_t*) = res;
|
||||
else if (flags & LONG)
|
||||
} else if (flags & LONG) {
|
||||
*va_arg(ap, long*) = res;
|
||||
else if (flags & SHORT)
|
||||
} else if (flags & SHORT) {
|
||||
*va_arg(ap, short*) = res;
|
||||
else if (flags & SHORTSHORT)
|
||||
} else if (flags & SHORTSHORT) {
|
||||
*va_arg(ap, signed char*) = res;
|
||||
else
|
||||
} else {
|
||||
*va_arg(ap, int*) = res;
|
||||
}
|
||||
nassigned++;
|
||||
}
|
||||
nread += p - buf;
|
||||
|
@ -659,10 +719,12 @@ literal:
|
|||
break;
|
||||
}
|
||||
}
|
||||
allocation_failure:
|
||||
input_failure:
|
||||
free(allocation);
|
||||
if (nassigned == 0) nassigned = -1;
|
||||
match_failure:
|
||||
return (nassigned);
|
||||
return nassigned;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -671,7 +733,7 @@ match_failure:
|
|||
* closing `]'. The table has a 1 wherever characters should be
|
||||
* considered part of the scanset.
|
||||
*/
|
||||
static u_char* __sccl(char* tab, u_char* fmt) {
|
||||
static const unsigned char* __sccl(char* tab, const unsigned char* fmt) {
|
||||
int c, n, v;
|
||||
|
||||
/* first `clear' the whole table */
|
||||
|
@ -744,7 +806,7 @@ static u_char* __sccl(char* tab, u_char* fmt) {
|
|||
break;
|
||||
|
||||
case ']': /* end of scanset */
|
||||
return (fmt);
|
||||
return fmt;
|
||||
|
||||
default: /* just another character */
|
||||
c = n;
|
|
@ -1016,6 +1016,138 @@ TEST(STDIO_TEST, swscanf_ccl) {
|
|||
CheckScanf(swscanf, L"+,-/.", L"%[+--/]", 1, "+,-/");
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
static void CheckScanfM(int sscanf_fn(const T1*, const T1*, ...),
|
||||
const T1* input, const T1* fmt,
|
||||
int expected_count, const T2* expected_string) {
|
||||
T2* result = nullptr;
|
||||
ASSERT_EQ(expected_count, sscanf_fn(input, fmt, &result)) << fmt;
|
||||
if (expected_string == nullptr) {
|
||||
ASSERT_EQ(nullptr, result);
|
||||
} else {
|
||||
ASSERT_STREQ(expected_string, result) << fmt;
|
||||
}
|
||||
free(result);
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_mc) {
|
||||
char* p1 = nullptr;
|
||||
char* p2 = nullptr;
|
||||
ASSERT_EQ(2, sscanf("hello", "%mc%mc", &p1, &p2));
|
||||
ASSERT_EQ('h', *p1);
|
||||
ASSERT_EQ('e', *p2);
|
||||
free(p1);
|
||||
free(p2);
|
||||
|
||||
p1 = nullptr;
|
||||
ASSERT_EQ(1, sscanf("hello", "%4mc", &p1));
|
||||
ASSERT_EQ('h', p1[0]);
|
||||
ASSERT_EQ('e', p1[1]);
|
||||
ASSERT_EQ('l', p1[2]);
|
||||
ASSERT_EQ('l', p1[3]);
|
||||
free(p1);
|
||||
|
||||
p1 = nullptr;
|
||||
ASSERT_EQ(1, sscanf("hello world", "%30mc", &p1));
|
||||
ASSERT_EQ('h', p1[0]);
|
||||
ASSERT_EQ('e', p1[1]);
|
||||
ASSERT_EQ('l', p1[2]);
|
||||
ASSERT_EQ('l', p1[3]);
|
||||
ASSERT_EQ('o', p1[4]);
|
||||
free(p1);
|
||||
}
|
||||
|
||||
|
||||
TEST(STDIO_TEST, sscanf_mlc) {
|
||||
// This is so useless that clang doesn't even believe it exists...
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
|
||||
#pragma clang diagnostic ignored "-Wformat-extra-args"
|
||||
|
||||
wchar_t* p1 = nullptr;
|
||||
wchar_t* p2 = nullptr;
|
||||
ASSERT_EQ(2, sscanf("hello", "%mlc%mlc", &p1, &p2));
|
||||
ASSERT_EQ(L'h', *p1);
|
||||
ASSERT_EQ(L'e', *p2);
|
||||
free(p1);
|
||||
free(p2);
|
||||
|
||||
p1 = nullptr;
|
||||
ASSERT_EQ(1, sscanf("hello", "%4mlc", &p1));
|
||||
ASSERT_EQ(L'h', p1[0]);
|
||||
ASSERT_EQ(L'e', p1[1]);
|
||||
ASSERT_EQ(L'l', p1[2]);
|
||||
ASSERT_EQ(L'l', p1[3]);
|
||||
free(p1);
|
||||
|
||||
p1 = nullptr;
|
||||
ASSERT_EQ(1, sscanf("hello world", "%30mlc", &p1));
|
||||
ASSERT_EQ(L'h', p1[0]);
|
||||
ASSERT_EQ(L'e', p1[1]);
|
||||
ASSERT_EQ(L'l', p1[2]);
|
||||
ASSERT_EQ(L'l', p1[3]);
|
||||
ASSERT_EQ(L'o', p1[4]);
|
||||
free(p1);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
|
||||
TEST(STDIO_TEST, sscanf_ms) {
|
||||
CheckScanfM(sscanf, "hello", "%ms", 1, "hello");
|
||||
CheckScanfM(sscanf, "hello", "%4ms", 1, "hell");
|
||||
CheckScanfM(sscanf, "hello world", "%30ms", 1, "hello");
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_mls) {
|
||||
CheckScanfM(sscanf, "hello", "%mls", 1, L"hello");
|
||||
CheckScanfM(sscanf, "hello", "%4mls", 1, L"hell");
|
||||
CheckScanfM(sscanf, "hello world", "%30mls", 1, L"hello");
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_m_ccl) {
|
||||
CheckScanfM(sscanf, "hello", "%m[a-z]", 1, "hello");
|
||||
CheckScanfM(sscanf, "hello", "%4m[a-z]", 1, "hell");
|
||||
CheckScanfM(sscanf, "hello world", "%30m[a-z]", 1, "hello");
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_ml_ccl) {
|
||||
CheckScanfM(sscanf, "hello", "%ml[a-z]", 1, L"hello");
|
||||
CheckScanfM(sscanf, "hello", "%4ml[a-z]", 1, L"hell");
|
||||
CheckScanfM(sscanf, "hello world", "%30ml[a-z]", 1, L"hello");
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_ls) {
|
||||
wchar_t w[32] = {};
|
||||
ASSERT_EQ(1, sscanf("hello world", "%ls", w));
|
||||
ASSERT_EQ(L"hello", std::wstring(w));
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_ls_suppress) {
|
||||
ASSERT_EQ(0, sscanf("hello world", "%*ls %*ls"));
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_ls_n) {
|
||||
setlocale(LC_ALL, "C.UTF-8");
|
||||
wchar_t w[32] = {};
|
||||
int pos = 0;
|
||||
ASSERT_EQ(1, sscanf("\xc4\x80", "%ls%n", w, &pos));
|
||||
ASSERT_EQ(static_cast<wchar_t>(256), w[0]);
|
||||
ASSERT_EQ(2, pos);
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, sscanf_ls_realloc) {
|
||||
// This is so useless that clang doesn't even believe it exists...
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
|
||||
#pragma clang diagnostic ignored "-Wformat-extra-args"
|
||||
wchar_t* p1 = nullptr;
|
||||
wchar_t* p2 = nullptr;
|
||||
ASSERT_EQ(2, sscanf("1234567890123456789012345678901234567890 world", "%mls %mls", &p1, &p2));
|
||||
ASSERT_EQ(L"1234567890123456789012345678901234567890", std::wstring(p1));
|
||||
ASSERT_EQ(L"world", std::wstring(p2));
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=202240
|
||||
TEST(STDIO_TEST, scanf_wscanf_EOF) {
|
||||
EXPECT_EQ(0, sscanf("b", "ab"));
|
||||
|
|
Loading…
Reference in a new issue