libc: Implement N2630.

This adds formatted input/output of binary integer numbers to the printf(), scanf(), and strtol() families, including their wide-character counterparts.

Reviewed by:	imp, emaste
Differential Revision:	https://reviews.freebsd.org/D41511

(cherry picked from commit d9dc1603d6)

libc: Add unit tests for N2630 and possible collateral damage.

Reviewed by:	imp, emaste
Differential Revision:	https://reviews.freebsd.org/D41512

(cherry picked from commit b9385720f3)

libc: Document support for binary integers.

Reviewed by:	debdrup, emaste
Differential Revision:	https://reviews.freebsd.org/D41522

(cherry picked from commit 76edfabbec)

libc: Fix fixed-width case in the new integer parser.

Fixes:		d9dc1603d6
Differential Revision:	https://reviews.freebsd.org/D41622

(cherry picked from commit aca3bd1602)

libc: Add a wide version of snprintf_test.

Reviewed by:	imp, emaste
Differential Revision:	https://reviews.freebsd.org/D41726

(cherry picked from commit 4ec9ee9912)

libc: Suppress format checks on printf() / scanf() tests.

Reviewed by:	jrtc27, markj, emaste
Differential Revision:	https://reviews.freebsd.org/D41727

(cherry picked from commit 294bd2827e)

Approved by:	re (gjb)
This commit is contained in:
Dag-Erling Smørgrav 2023-08-28 15:32:23 +00:00
parent 70fbe797c0
commit bae932999e
29 changed files with 1315 additions and 259 deletions

View file

@ -94,6 +94,12 @@ ATF_TC_BODY(strtol_base, tc)
{ "01234567", 342391, 0, NULL },
{ "0123456789", 123456789, 10, NULL },
{ "0x75bcd15", 123456789, 0, NULL },
#ifdef __FreeBSD__
{ "0x", 0, 0, "x" },
{ "0b111010110111100110100010101", 123456789, 0, NULL },
{ "0b0123", 1, 0, "23" },
{ "0b", 0, 0, "b" },
#endif
};
long long int lli;

View file

@ -91,6 +91,13 @@ _FUNCNAME(const char *nptr, char **endptr, int base)
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = (c == '0' ? 8 : 10);

View file

@ -87,6 +87,13 @@ _FUNCNAME(const char *nptr, char **endptr, int base)
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = (c == '0' ? 8 : 10);

View file

@ -86,6 +86,13 @@ wcstoimax_l(const wchar_t * __restrict nptr, wchar_t ** __restrict endptr,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == L'0' && (*s == L'b' || *s == L'B') &&
(s[1] >= L'0' && s[1] <= L'1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == L'0' ? 8 : 10;
acc = any = 0;

View file

@ -80,6 +80,13 @@ wcstol_l(const wchar_t * __restrict nptr, wchar_t ** __restrict endptr, int
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == L'0' && (*s == L'b' || *s == L'B') &&
(s[1] >= L'0' && s[1] <= L'1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == L'0' ? 8 : 10;
acc = any = 0;

View file

@ -86,6 +86,13 @@ wcstoll_l(const wchar_t * __restrict nptr, wchar_t ** __restrict endptr,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == L'0' && (*s == L'b' || *s == L'B') &&
(s[1] >= L'0' && s[1] <= L'1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == L'0' ? 8 : 10;
acc = any = 0;

View file

@ -80,6 +80,13 @@ wcstoul_l(const wchar_t * __restrict nptr, wchar_t ** __restrict endptr,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == L'0' && (*s == L'b' || *s == L'B') &&
(s[1] >= L'0' && s[1] <= L'1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == L'0' ? 8 : 10;
acc = any = 0;

View file

@ -86,6 +86,13 @@ wcstoull_l(const wchar_t * __restrict nptr, wchar_t ** __restrict endptr,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == L'0' && (*s == L'b' || *s == L'B') &&
(s[1] >= L'0' && s[1] <= L'1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == L'0' ? 8 : 10;
acc = any = 0;

View file

@ -86,6 +86,13 @@ wcstoumax_l(const wchar_t * __restrict nptr, wchar_t ** __restrict endptr,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == L'0' && (*s == L'b' || *s == L'B') &&
(s[1] >= L'0' && s[1] <= L'1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == L'0' ? 8 : 10;
acc = any = 0;

View file

@ -31,7 +31,7 @@
.\"
.\" @(#)printf.3 8.1 (Berkeley) 6/4/93
.\"
.Dd May 22, 2018
.Dd August 21, 2023
.Dt PRINTF 3
.Os
.Sh NAME
@ -212,6 +212,17 @@ and
.Cm u
conversions, this option has no effect.
For
.Cm b
and
.Cm B
conversions, a non-zero result has the string
.Ql 0b
(or
.Ql 0B
for
.Cm B
conversions) prepended to it.
For
.Cm o
conversions, the precision of the number is increased to force the first
character of the output string to a zero.
@ -245,7 +256,7 @@ For all conversions except
.Cm n ,
the converted value is padded on the left with zeros rather than blanks.
If a precision is given with a numeric conversion
.Cm ( d , i , o , u , i , x ,
.Cm ( b , B , d , i , o , u , i , x ,
and
.Cm X ) ,
the
@ -301,7 +312,7 @@ followed by an
optional digit string.
If the digit string is omitted, the precision is taken as zero.
This gives the minimum number of digits to appear for
.Cm d , i , o , u , x ,
.Cm b , B , d , i , o , u , x ,
and
.Cm X
conversions, the number of digits to appear after the decimal-point for
@ -319,12 +330,12 @@ conversions.
.It
An optional length modifier, that specifies the size of the argument.
The following length modifiers are valid for the
.Cm d , i , n , o , u , x ,
.Cm b , B , d , i , n , o , u , x ,
or
.Cm X
conversion:
.Bl -column ".Cm q Em (deprecated)" ".Vt signed char" ".Vt unsigned long long" ".Vt long long *"
.It Sy Modifier Ta Cm d , i Ta Cm o , u , x , X Ta Cm n
.It Sy Modifier Ta Cm d , i Ta Cm b , B , o , u , x , X Ta Cm n
.It Cm hh Ta Vt "signed char" Ta Vt "unsigned char" Ta Vt "signed char *"
.It Cm h Ta Vt short Ta Vt "unsigned short" Ta Vt "short *"
.It Cm l No (ell) Ta Vt long Ta Vt "unsigned long" Ta Vt "long *"
@ -339,7 +350,7 @@ Note:
the
.Cm t
modifier, when applied to a
.Cm o , u , x ,
.Cm b , B , o , u , x ,
or
.Cm X
conversion, indicates that the argument is of an unsigned type
@ -403,11 +414,16 @@ If a single format directive mixes positional
and non-positional arguments, the results are undefined.
.Pp
The conversion specifiers and their meanings are:
.Bl -tag -width ".Cm diouxX"
.It Cm diouxX
.Bl -tag -width ".Cm bBdiouxX"
.It Cm bBdiouxX
The
.Vt int
(or appropriate variant) argument is converted to signed decimal
(or appropriate variant) argument is converted to
unsigned binary
.Cm ( b
and
.Cm B ) ,
signed decimal
.Cm ( d
and
.Cm i ) ,

View file

@ -194,6 +194,13 @@ __ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs)
} while (sval != 0);
break;
case 2:
do {
*--cp = to_char(val & 1);
val >>= 1;
} while (val);
break;
case 8:
do {
*--cp = to_char(val & 7);
@ -244,6 +251,13 @@ __ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs)
} while (sval != 0);
break;
case 2:
do {
*--cp = to_char(val & 1);
val >>= 1;
} while (val);
break;
case 8:
do {
*--cp = to_char(val & 7);

View file

@ -31,7 +31,7 @@
.\"
.\" @(#)scanf.3 8.2 (Berkeley) 12/11/93
.\"
.Dd April 2, 2022
.Dd August 21, 2023
.Dt SCANF 3
.Os
.Sh NAME
@ -141,7 +141,7 @@ The conversion that follows occurs as usual, but no pointer is used;
the result of the conversion is simply discarded.
.It Cm hh
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -150,7 +150,7 @@ and the next pointer is a pointer to a
.Vt int ) .
.It Cm h
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -159,7 +159,7 @@ and the next pointer is a pointer to a
.Vt int ) .
.It Cm l No (ell)
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -185,7 +185,7 @@ and the next pointer is a pointer to an array of
.Vt char ) .
.It Cm ll No (ell ell)
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -201,7 +201,7 @@ and the next pointer is a pointer to
.Vt "long double" .
.It Cm j
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -210,7 +210,7 @@ and the next pointer is a pointer to a
.Vt int ) .
.It Cm t
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -219,7 +219,7 @@ and the next pointer is a pointer to a
.Vt int ) .
.It Cm z
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -229,7 +229,7 @@ and the next pointer is a pointer to a
.It Cm q
(deprecated.)
Indicates that the conversion will be one of
.Cm dioux
.Cm bdioux
or
.Cm n
and the next pointer is a pointer to a
@ -273,6 +273,10 @@ matches a single input
.Ql %
character.
No conversion is done, and assignment does not occur.
.It Cm b , B
Matches an optionally signed binary integer;
the next pointer must be a pointer to
.Vt "unsigned int" .
.It Cm d
Matches an optionally signed decimal integer;
the next pointer must be a pointer to
@ -281,7 +285,12 @@ the next pointer must be a pointer to
Matches an optionally signed integer;
the next pointer must be a pointer to
.Vt int .
The integer is read in base 16 if it begins
The integer is read
in base 2 if it begins with
.Ql 0b
or
.Ql 0B ,
in base 16 if it begins
with
.Ql 0x
or

View file

@ -613,6 +613,19 @@ reswitch: switch (ch) {
case 'z':
flags |= SIZET;
goto rflag;
case 'B':
case 'b':
if (flags & INTMAX_SIZE)
ujval = UJARG();
else
ulval = UARG();
base = 2;
/* leading 0b/B only if non-zero */
if (flags & ALT &&
(flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
ox[1] = ch;
goto nosign;
break;
case 'C':
flags |= LONGINT;
/*FALLTHROUGH*/

View file

@ -6,6 +6,8 @@
*
* Copyright (c) 2011 The FreeBSD Foundation
*
* Copyright (c) 2023 Dag-Erling Smørgrav
*
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
@ -80,16 +82,6 @@ static char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93";
#define SHORTSHORT 0x4000 /* hh: char */
#define UNSIGNED 0x8000 /* %[oupxX] conversions */
/*
* The following are used in integral conversions only:
* SIGNOK, NDIGITS, PFXOK, and NZDIGITS
*/
#define SIGNOK 0x40 /* +/- is (still) legal */
#define NDIGITS 0x80 /* no digits detected */
#define PFXOK 0x100 /* 0x prefix is (still) legal */
#define NZDIGITS 0x200 /* no zero digits detected */
#define HAVESIGN 0x10000 /* sign detected */
/*
* Conversion types.
*/
@ -307,129 +299,160 @@ convert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale)
return (n);
}
enum parseint_state {
begin,
havesign,
havezero,
haveprefix,
any,
};
static __inline int
parseint_fsm(int c, enum parseint_state *state, int *base)
{
switch (c) {
case '+':
case '-':
if (*state == begin) {
*state = havesign;
return 1;
}
break;
case '0':
if (*state == begin || *state == havesign) {
*state = havezero;
} else {
*state = any;
}
return 1;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
if (*state == havezero && *base == 0) {
*base = 8;
}
/* FALL THROUGH */
case '8':
case '9':
if (*state == begin ||
*state == havesign) {
if (*base == 0) {
*base = 10;
}
}
if (*state == begin ||
*state == havesign ||
*state == havezero ||
*state == haveprefix ||
*state == any) {
if (*base > c - '0') {
*state = any;
return 1;
}
}
break;
case 'b':
if (*state == havezero) {
if (*base == 0 || *base == 2) {
*state = haveprefix;
*base = 2;
return 1;
}
}
/* FALL THROUGH */
case 'a':
case 'c':
case 'd':
case 'e':
case 'f':
if (*state == begin ||
*state == havesign ||
*state == havezero ||
*state == haveprefix ||
*state == any) {
if (*base > c - 'a' + 10) {
*state = any;
return 1;
}
}
break;
case 'B':
if (*state == havezero) {
if (*base == 0 || *base == 2) {
*state = haveprefix;
*base = 2;
return 1;
}
}
/* FALL THROUGH */
case 'A':
case 'C':
case 'D':
case 'E':
case 'F':
if (*state == begin ||
*state == havesign ||
*state == havezero ||
*state == haveprefix ||
*state == any) {
if (*base > c - 'A' + 10) {
*state = any;
return 1;
}
}
break;
case 'x':
case 'X':
if (*state == havezero) {
if (*base == 0 || *base == 16) {
*state = haveprefix;
*base = 16;
return 1;
}
}
break;
}
return 0;
}
/*
* Read an integer, storing it in buf. The only relevant bit in the
* flags argument is PFXOK.
* Read an integer, storing it in buf.
*
* Return 0 on a match failure, and the number of characters read
* otherwise.
*/
static __inline int
parseint(FILE *fp, char * __restrict buf, int width, int base, int flags)
parseint(FILE *fp, char * __restrict buf, int width, int base)
{
/* `basefix' is used to avoid `if' tests */
static const short basefix[17] =
{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
enum parseint_state state = begin;
char *p;
int c;
flags |= SIGNOK | NDIGITS | NZDIGITS;
for (p = buf; width; width--) {
c = *fp->_p;
/*
* Switch on the character; `goto ok' if we accept it
* as a part of number.
*/
switch (c) {
/*
* The digit 0 is always legal, but is special. For
* %i conversions, if no digits (zero or nonzero) have
* been scanned (only signs), we will have base==0.
* In that case, we should set it to 8 and enable 0x
* prefixing. Also, if we have not scanned zero
* digits before this, do not turn off prefixing
* (someone else will turn it off if we have scanned
* any nonzero digits).
*/
case '0':
if (base == 0) {
base = 8;
flags |= PFXOK;
}
if (flags & NZDIGITS)
flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
else
flags &= ~(SIGNOK|PFXOK|NDIGITS);
goto ok;
/* 1 through 7 always legal */
case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
base = basefix[base];
flags &= ~(SIGNOK | PFXOK | NDIGITS);
goto ok;
/* digits 8 and 9 ok iff decimal or hex */
case '8': case '9':
base = basefix[base];
if (base <= 8)
break; /* not legal here */
flags &= ~(SIGNOK | PFXOK | NDIGITS);
goto ok;
/* letters ok iff hex */
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
/* no need to fix base here */
if (base <= 10)
break; /* not legal here */
flags &= ~(SIGNOK | PFXOK | NDIGITS);
goto ok;
/* sign ok only as first character */
case '+': case '-':
if (flags & SIGNOK) {
flags &= ~SIGNOK;
flags |= HAVESIGN;
goto ok;
}
c = __sgetc(fp);
if (c == EOF)
break;
/*
* x ok iff flag still set & 2nd char (or 3rd char if
* we have a sign).
*/
case 'x': case 'X':
if (flags & PFXOK && p ==
buf + 1 + !!(flags & HAVESIGN)) {
base = 16; /* if %i */
flags &= ~PFXOK;
goto ok;
}
if (!parseint_fsm(c, &state, &base))
break;
}
/*
* If we got here, c is not a legal character for a
* number. Stop accumulating digits.
*/
break;
ok:
/*
* c is legal: store it and look at the next.
*/
*p++ = c;
if (--fp->_r > 0)
fp->_p++;
else if (__srefill(fp))
break; /* EOF */
}
/*
* If we had only a sign, it is no good; push back the sign.
* If the number ends in `x', it was [sign] '0' 'x', so push
* back the x and treat it as [sign] '0'.
* If we only had a sign, push it back. If we only had a 0b or 0x
* prefix (possibly preceded by a sign), we view it as "0" and
* push back the letter. In all other cases, if we stopped
* because we read a non-number character, push it back.
*/
if (flags & NDIGITS) {
if (p > buf)
(void) __ungetc(*(u_char *)--p, fp);
return (0);
}
c = ((u_char *)p)[-1];
if (c == 'x' || c == 'X') {
--p;
if (state == havesign) {
p--;
(void) __ungetc(*(u_char *)p, fp);
} else if (state == haveprefix) {
p--;
(void) __ungetc(c, fp);
} else if (width && c != EOF) {
(void) __ungetc(c, fp);
}
return (p - buf);
@ -554,6 +577,13 @@ literal:
/*
* Conversions.
*/
case 'B':
case 'b':
c = CT_INT;
flags |= UNSIGNED;
base = 2;
break;
case 'd':
c = CT_INT;
base = 10;
@ -578,7 +608,6 @@ literal:
case 'X':
case 'x':
flags |= PFXOK; /* enable 0x prefixing */
c = CT_INT;
flags |= UNSIGNED;
base = 16;
@ -613,7 +642,7 @@ literal:
break;
case 'p': /* pointer format is like hex */
flags |= POINTER | PFXOK;
flags |= POINTER;
c = CT_INT; /* assumes sizeof(uintmax_t) */
flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
base = 16;
@ -738,7 +767,7 @@ literal:
width = sizeof(buf) - 2;
width++;
#endif
nr = parseint(fp, buf, width, base, flags);
nr = parseint(fp, buf, width, base);
if (nr == 0)
goto match_failure;
if ((flags & SUPPRESS) == 0) {

View file

@ -684,6 +684,19 @@ reswitch: switch (ch) {
case 'z':
flags |= SIZET;
goto rflag;
case 'B':
case 'b':
if (flags & INTMAX_SIZE)
ujval = UJARG();
else
ulval = UARG();
base = 2;
/* leading 0b/B only if non-zero */
if (flags & ALT &&
(flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
ox[1] = ch;
goto nosign;
break;
case 'C':
flags |= LONGINT;
/*FALLTHROUGH*/

View file

@ -9,6 +9,8 @@
*
* Copyright (c) 2011 The FreeBSD Foundation
*
* Copyright (c) 2023 Dag-Erling Smørgrav
*
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
@ -78,16 +80,6 @@ static char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93";
#define SHORTSHORT 0x4000 /* hh: char */
#define UNSIGNED 0x8000 /* %[oupxX] conversions */
/*
* The following are used in integral conversions only:
* SIGNOK, NDIGITS, PFXOK, and NZDIGITS
*/
#define SIGNOK 0x40 /* +/- is (still) legal */
#define NDIGITS 0x80 /* no digits detected */
#define PFXOK 0x100 /* 0x prefix is (still) legal */
#define NZDIGITS 0x200 /* no zero digits detected */
#define HAVESIGN 0x10000 /* sign detected */
/*
* Conversion types.
*/
@ -289,128 +281,161 @@ convert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale)
return (nread);
}
enum parseint_state {
begin,
havesign,
havezero,
haveprefix,
any,
};
static __inline int
parseint_fsm(wchar_t c, enum parseint_state *state, int *base)
{
switch (c) {
case '+':
case '-':
if (*state == begin) {
*state = havesign;
return 1;
}
break;
case '0':
if (*state == begin || *state == havesign) {
*state = havezero;
} else {
*state = any;
}
return 1;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
if (*state == havezero && *base == 0) {
*base = 8;
}
/* FALL THROUGH */
case '8':
case '9':
if (*state == begin ||
*state == havesign) {
if (*base == 0) {
*base = 10;
}
}
if (*state == begin ||
*state == havesign ||
*state == havezero ||
*state == haveprefix ||
*state == any) {
if (*base > c - '0') {
*state = any;
return 1;
}
}
break;
case 'b':
if (*state == havezero) {
if (*base == 0 || *base == 2) {
*state = haveprefix;
*base = 2;
return 1;
}
}
/* FALL THROUGH */
case 'a':
case 'c':
case 'd':
case 'e':
case 'f':
if (*state == begin ||
*state == havesign ||
*state == havezero ||
*state == haveprefix ||
*state == any) {
if (*base > c - 'a' + 10) {
*state = any;
return 1;
}
}
break;
case 'B':
if (*state == havezero) {
if (*base == 0 || *base == 2) {
*state = haveprefix;
*base = 2;
return 1;
}
}
/* FALL THROUGH */
case 'A':
case 'C':
case 'D':
case 'E':
case 'F':
if (*state == begin ||
*state == havesign ||
*state == havezero ||
*state == haveprefix ||
*state == any) {
if (*base > c - 'A' + 10) {
*state = any;
return 1;
}
}
break;
case 'x':
case 'X':
if (*state == havezero) {
if (*base == 0 || *base == 16) {
*state = haveprefix;
*base = 16;
return 1;
}
}
break;
}
return 0;
}
/*
* Read an integer, storing it in buf. The only relevant bit in the
* flags argument is PFXOK.
* Read an integer, storing it in buf.
*
* Return 0 on a match failure, and the number of characters read
* otherwise.
*/
static __inline int
parseint(FILE *fp, wchar_t *buf, int width, int base, int flags,
parseint(FILE *fp, wchar_t * __restrict buf, int width, int base,
locale_t locale)
{
/* `basefix' is used to avoid `if' tests */
static const short basefix[17] =
{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
enum parseint_state state = begin;
wchar_t *wcp;
int c;
flags |= SIGNOK | NDIGITS | NZDIGITS;
for (wcp = buf; width; width--) {
c = __fgetwc(fp, locale);
/*
* Switch on the character; `goto ok' if we accept it
* as a part of number.
*/
switch (c) {
/*
* The digit 0 is always legal, but is special. For
* %i conversions, if no digits (zero or nonzero) have
* been scanned (only signs), we will have base==0.
* In that case, we should set it to 8 and enable 0x
* prefixing. Also, if we have not scanned zero
* digits before this, do not turn off prefixing
* (someone else will turn it off if we have scanned
* any nonzero digits).
*/
case '0':
if (base == 0) {
base = 8;
flags |= PFXOK;
}
if (flags & NZDIGITS)
flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
else
flags &= ~(SIGNOK|PFXOK|NDIGITS);
goto ok;
/* 1 through 7 always legal */
case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
base = basefix[base];
flags &= ~(SIGNOK | PFXOK | NDIGITS);
goto ok;
/* digits 8 and 9 ok iff decimal or hex */
case '8': case '9':
base = basefix[base];
if (base <= 8)
break; /* not legal here */
flags &= ~(SIGNOK | PFXOK | NDIGITS);
goto ok;
/* letters ok iff hex */
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
/* no need to fix base here */
if (base <= 10)
break; /* not legal here */
flags &= ~(SIGNOK | PFXOK | NDIGITS);
goto ok;
/* sign ok only as first character */
case '+': case '-':
if (flags & SIGNOK) {
flags &= ~SIGNOK;
flags |= HAVESIGN;
goto ok;
}
if (c == WEOF)
break;
/*
* x ok iff flag still set & 2nd char (or 3rd char if
* we have a sign).
*/
case 'x': case 'X':
if (flags & PFXOK && wcp ==
buf + 1 + !!(flags & HAVESIGN)) {
base = 16; /* if %i */
flags &= ~PFXOK;
goto ok;
}
if (!parseint_fsm(c, &state, &base))
break;
}
/*
* If we got here, c is not a legal character for a
* number. Stop accumulating digits.
*/
if (c != WEOF)
__ungetwc(c, fp, locale);
break;
ok:
/*
* c is legal: store it and look at the next.
*/
*wcp++ = (wchar_t)c;
}
/*
* If we had only a sign, it is no good; push back the sign.
* If the number ends in `x', it was [sign] '0' 'x', so push
* back the x and treat it as [sign] '0'.
* If we only had a sign, push it back. If we only had a 0b or 0x
* prefix (possibly preceded by a sign), we view it as "0" and
* push back the letter. In all other cases, if we stopped
* because we read a non-number character, push it back.
*/
if (flags & NDIGITS) {
if (wcp > buf)
__ungetwc(*--wcp, fp, locale);
return (0);
}
c = wcp[-1];
if (c == 'x' || c == 'X') {
--wcp;
if (state == havesign) {
wcp--;
__ungetwc(*wcp, fp, locale);
} else if (state == haveprefix) {
wcp--;
__ungetwc(c, fp, locale);
} else if (width && c != WEOF) {
__ungetwc(c, fp, locale);
}
return (wcp - buf);
@ -536,6 +561,13 @@ literal:
/*
* Conversions.
*/
case 'B':
case 'b':
c = CT_INT;
flags |= UNSIGNED;
base = 2;
break;
case 'd':
c = CT_INT;
base = 10;
@ -560,7 +592,6 @@ literal:
case 'X':
case 'x':
flags |= PFXOK; /* enable 0x prefixing */
c = CT_INT;
flags |= UNSIGNED;
base = 16;
@ -606,7 +637,7 @@ literal:
break;
case 'p': /* pointer format is like hex */
flags |= POINTER | PFXOK;
flags |= POINTER;
c = CT_INT; /* assumes sizeof(uintmax_t) */
flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
base = 16;
@ -716,7 +747,7 @@ literal:
sizeof(*buf) - 1)
width = sizeof(buf) / sizeof(*buf) - 1;
nr = parseint(fp, buf, width, base, flags, locale);
nr = parseint(fp, buf, width, base, locale);
if (nr == 0)
goto match_failure;
if ((flags & SUPPRESS) == 0) {

View file

@ -87,6 +87,13 @@ strtoimax_l(const char * __restrict nptr, char ** __restrict endptr, int base,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;

View file

@ -31,7 +31,7 @@
.\"
.\" @(#)strtol.3 8.1 (Berkeley) 6/4/93
.\"
.Dd November 28, 2001
.Dd August 21, 2023
.Dt STRTOL 3
.Os
.Sh NAME
@ -108,6 +108,8 @@ If
.Fa base
is zero or 16,
the string may then include a
.Dq Li 0b
prefix, and the number will be read in base 2; or it may include a
.Dq Li 0x
prefix,
and the number will be read in base 16; otherwise, a zero

View file

@ -87,6 +87,13 @@ strtol_l(const char * __restrict nptr, char ** __restrict endptr, int base,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;

View file

@ -63,8 +63,9 @@ strtoll_l(const char * __restrict nptr, char ** __restrict endptr, int base,
/*
* Skip white space and pick up leading +/- sign if any.
* If base is 0, allow 0x for hex and 0 for octal, else
* assume decimal; if base is already 16, allow 0x.
* If base is 0, allow 0b for binary, 0x for hex, and 0 for
* octal, else assume decimal; if base is already 2, allow
* 0b; if base is already 16, allow 0x.
*/
s = nptr;
do {
@ -87,6 +88,13 @@ strtoll_l(const char * __restrict nptr, char ** __restrict endptr, int base,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;

View file

@ -31,7 +31,7 @@
.\"
.\" @(#)strtoul.3 8.1 (Berkeley) 6/4/93
.\"
.Dd November 28, 2001
.Dd August 21, 2023
.Dt STRTOUL 3
.Os
.Sh NAME
@ -108,6 +108,8 @@ If
.Fa base
is zero or 16,
the string may then include a
.Dq Li 0b
prefix, and the number will be read in base 2; or it may include a
.Dq Li 0x
prefix,
and the number will be read in base 16; otherwise, a zero

View file

@ -84,6 +84,13 @@ strtoul_l(const char * __restrict nptr, char ** __restrict endptr, int base, loc
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;

View file

@ -85,6 +85,13 @@ strtoull_l(const char * __restrict nptr, char ** __restrict endptr, int base,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;

View file

@ -85,6 +85,13 @@ strtoumax_l(const char * __restrict nptr, char ** __restrict endptr, int base,
s += 2;
base = 16;
}
if ((base == 0 || base == 2) &&
c == '0' && (*s == 'b' || *s == 'B') &&
(s[1] >= '0' && s[1] <= '1')) {
c = s[1];
s += 2;
base = 2;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;

View file

@ -16,6 +16,10 @@ ATF_TESTS_C+= print_positional_test
ATF_TESTS_C+= printbasic_test
ATF_TESTS_C+= printfloat_test
ATF_TESTS_C+= scanfloat_test
ATF_TESTS_C+= snprintf_test
ATF_TESTS_C+= sscanf_test
ATF_TESTS_C+= swprintf_test
ATF_TESTS_C+= swscanf_test
SRCS.fopen2_test= fopen_test.c
@ -34,9 +38,15 @@ LIBADD.eintr_test+= md
LIBADD.printfloat_test+= m
LIBADD.scanfloat_test+= m
# Older toolchains won't understand C23 %b, %wN, %wfN
PROG_OVERRIDE_VARS+= NO_WFORMAT
NO_WFORMAT.snprintf_test=
NO_WFORMAT.sscanf_test=
NO_WFORMAT.swprintf_test=
NO_WFORMAT.swscanf_test=
.if ${COMPILER_TYPE} == "gcc"
# 90: use of assignment suppression and length modifier together in scanf format
PROG_OVERRIDE_VARS+= NO_WFORMAT
NO_WFORMAT.scanfloat_test=
.endif

View file

@ -0,0 +1,139 @@
/*-
* Copyright (c) 2023 Dag-Erling Smørgrav
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <limits.h>
#include <locale.h>
#include <stdint.h>
#include <stdio.h>
#include <atf-c.h>
#ifndef nitems
#define nitems(a) (sizeof(a) / sizeof(a[0]))
#endif
#define SNPRINTF_TEST(output, format, ...) \
do { \
char buf[256]; \
assert(strlen(output) < nitems(buf)); \
int ret = snprintf(buf, nitems(buf), format, \
__VA_ARGS__); \
ATF_CHECK_EQ(strlen(output), ret); \
if (ret > 0) { \
ATF_CHECK_EQ(0, strcmp(output, buf)); \
} \
} while (0)
ATF_TC_WITHOUT_HEAD(snprintf_b);
ATF_TC_BODY(snprintf_b, tc)
{
SNPRINTF_TEST("0", "%b", 0);
SNPRINTF_TEST(" 0", "%12b", 0);
SNPRINTF_TEST("000000000000", "%012b", 0);
SNPRINTF_TEST("1", "%b", 1);
SNPRINTF_TEST(" 1", "%12b", 1);
SNPRINTF_TEST("000000000001", "%012b", 1);
SNPRINTF_TEST("1111111111111111111111111111111", "%b", INT_MAX);
SNPRINTF_TEST("0", "%#b", 0);
SNPRINTF_TEST(" 0", "%#12b", 0);
SNPRINTF_TEST("000000000000", "%#012b", 0);
SNPRINTF_TEST("0b1", "%#b", 1);
SNPRINTF_TEST(" 0b1", "%#12b", 1);
SNPRINTF_TEST("0b0000000001", "%#012b", 1);
SNPRINTF_TEST("0b1111111111111111111111111111111", "%#b", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(snprintf_B);
ATF_TC_BODY(snprintf_B, tc)
{
SNPRINTF_TEST("0", "%B", 0);
SNPRINTF_TEST(" 0", "%12B", 0);
SNPRINTF_TEST("000000000000", "%012B", 0);
SNPRINTF_TEST("1", "%B", 1);
SNPRINTF_TEST(" 1", "%12B", 1);
SNPRINTF_TEST("000000000001", "%012B", 1);
SNPRINTF_TEST("1111111111111111111111111111111", "%B", INT_MAX);
SNPRINTF_TEST("0", "%#B", 0);
SNPRINTF_TEST(" 0", "%#12B", 0);
SNPRINTF_TEST("000000000000", "%#012B", 0);
SNPRINTF_TEST("0B1", "%#B", 1);
SNPRINTF_TEST(" 0B1", "%#12B", 1);
SNPRINTF_TEST("0B0000000001", "%#012B", 1);
SNPRINTF_TEST("0B1111111111111111111111111111111", "%#B", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(snprintf_d);
ATF_TC_BODY(snprintf_d, tc)
{
SNPRINTF_TEST("0", "%d", 0);
SNPRINTF_TEST(" 0", "%12d", 0);
SNPRINTF_TEST("000000000000", "%012d", 0);
SNPRINTF_TEST("1", "%d", 1);
SNPRINTF_TEST(" 1", "%12d", 1);
SNPRINTF_TEST("000000000001", "%012d", 1);
SNPRINTF_TEST("2147483647", "%d", INT_MAX);
SNPRINTF_TEST(" 2147483647", "%12d", INT_MAX);
SNPRINTF_TEST("002147483647", "%012d", INT_MAX);
SNPRINTF_TEST("2,147,483,647", "%'d", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(snprintf_x);
ATF_TC_BODY(snprintf_x, tc)
{
SNPRINTF_TEST("0", "%x", 0);
SNPRINTF_TEST(" 0", "%12x", 0);
SNPRINTF_TEST("000000000000", "%012x", 0);
SNPRINTF_TEST("1", "%x", 1);
SNPRINTF_TEST(" 1", "%12x", 1);
SNPRINTF_TEST("000000000001", "%012x", 1);
SNPRINTF_TEST("7fffffff", "%x", INT_MAX);
SNPRINTF_TEST(" 7fffffff", "%12x", INT_MAX);
SNPRINTF_TEST("00007fffffff", "%012x", INT_MAX);
SNPRINTF_TEST("0", "%#x", 0);
SNPRINTF_TEST(" 0", "%#12x", 0);
SNPRINTF_TEST("000000000000", "%#012x", 0);
SNPRINTF_TEST("0x1", "%#x", 1);
SNPRINTF_TEST(" 0x1", "%#12x", 1);
SNPRINTF_TEST("0x0000000001", "%#012x", 1);
SNPRINTF_TEST("0x7fffffff", "%#x", INT_MAX);
SNPRINTF_TEST(" 0x7fffffff", "%#12x", INT_MAX);
SNPRINTF_TEST("0x007fffffff", "%#012x", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(snprintf_X);
ATF_TC_BODY(snprintf_X, tc)
{
SNPRINTF_TEST("0", "%X", 0);
SNPRINTF_TEST(" 0", "%12X", 0);
SNPRINTF_TEST("000000000000", "%012X", 0);
SNPRINTF_TEST("1", "%X", 1);
SNPRINTF_TEST(" 1", "%12X", 1);
SNPRINTF_TEST("000000000001", "%012X", 1);
SNPRINTF_TEST("7FFFFFFF", "%X", INT_MAX);
SNPRINTF_TEST(" 7FFFFFFF", "%12X", INT_MAX);
SNPRINTF_TEST("00007FFFFFFF", "%012X", INT_MAX);
SNPRINTF_TEST("0", "%#X", 0);
SNPRINTF_TEST(" 0", "%#12X", 0);
SNPRINTF_TEST("000000000000", "%#012X", 0);
SNPRINTF_TEST("0X1", "%#X", 1);
SNPRINTF_TEST(" 0X1", "%#12X", 1);
SNPRINTF_TEST("0X0000000001", "%#012X", 1);
SNPRINTF_TEST("0X7FFFFFFF", "%#X", INT_MAX);
SNPRINTF_TEST(" 0X7FFFFFFF", "%#12X", INT_MAX);
SNPRINTF_TEST("0X007FFFFFFF", "%#012X", INT_MAX);
}
ATF_TP_ADD_TCS(tp)
{
setlocale(LC_NUMERIC, "en_US.UTF-8");
ATF_TP_ADD_TC(tp, snprintf_b);
ATF_TP_ADD_TC(tp, snprintf_B);
ATF_TP_ADD_TC(tp, snprintf_d);
ATF_TP_ADD_TC(tp, snprintf_x);
ATF_TP_ADD_TC(tp, snprintf_X);
return (atf_no_error());
}

View file

@ -0,0 +1,266 @@
/*-
* Copyright (c) 2023 Dag-Erling Smørgrav
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <locale.h>
#include <stdint.h>
#include <stdio.h>
#include <atf-c.h>
static const struct sscanf_test_case {
char input[8];
struct {
int ret, val, len;
} b, o, d, x, i;
} sscanf_test_cases[] = {
// input binary octal decimal hexadecimal automatic
// all digits
{ "0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
{ "1", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
{ "2", { 0, 0, 0 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, },
{ "3", { 0, 0, 0 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, },
{ "4", { 0, 0, 0 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, },
{ "5", { 0, 0, 0 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, },
{ "6", { 0, 0, 0 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, },
{ "7", { 0, 0, 0 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, },
{ "8", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 8, 1 }, { 1, 8, 1 }, { 1, 8, 1 }, },
{ "9", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 9, 1 }, { 1, 9, 1 }, { 1, 9, 1 }, },
{ "A", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
{ "B", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
{ "C", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
{ "D", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
{ "E", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
{ "F", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
{ "X", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
{ "a", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
{ "b", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
{ "c", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
{ "d", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
{ "e", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
{ "f", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
{ "x", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
// all digits with leading zero
{ "00", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
{ "01", { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, },
{ "02", { 1, 0, 1 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, },
{ "03", { 1, 0, 1 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, },
{ "04", { 1, 0, 1 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, },
{ "05", { 1, 0, 1 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, },
{ "06", { 1, 0, 1 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, },
{ "07", { 1, 0, 1 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, },
{ "08", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 2 }, { 1, 8, 2 }, { 1, 0, 1 }, },
{ "09", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 2 }, { 1, 9, 2 }, { 1, 0, 1 }, },
{ "0A", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
{ "0B", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
{ "0C", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
{ "0D", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
{ "0E", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
{ "0F", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
{ "0X", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
{ "0a", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
{ "0b", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
{ "0c", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
{ "0d", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
{ "0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
{ "0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
{ "0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
// all digits with leading one
{ "10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, },
{ "11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, },
{ "12", { 1, 1, 1 }, { 1, 10, 2 }, { 1, 12, 2 }, { 1, 18, 2 }, { 1, 12, 2 }, },
{ "13", { 1, 1, 1 }, { 1, 11, 2 }, { 1, 13, 2 }, { 1, 19, 2 }, { 1, 13, 2 }, },
{ "14", { 1, 1, 1 }, { 1, 12, 2 }, { 1, 14, 2 }, { 1, 20, 2 }, { 1, 14, 2 }, },
{ "15", { 1, 1, 1 }, { 1, 13, 2 }, { 1, 15, 2 }, { 1, 21, 2 }, { 1, 15, 2 }, },
{ "16", { 1, 1, 1 }, { 1, 14, 2 }, { 1, 16, 2 }, { 1, 22, 2 }, { 1, 16, 2 }, },
{ "17", { 1, 1, 1 }, { 1, 15, 2 }, { 1, 17, 2 }, { 1, 23, 2 }, { 1, 17, 2 }, },
{ "18", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 18, 2 }, { 1, 24, 2 }, { 1, 18, 2 }, },
{ "19", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 19, 2 }, { 1, 25, 2 }, { 1, 19, 2 }, },
{ "1A", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
{ "1B", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
{ "1C", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
{ "1D", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
{ "1E", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
{ "1F", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
{ "1X", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
{ "1a", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
{ "1b", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
{ "1c", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
{ "1d", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
{ "1e", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
{ "1f", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
{ "1x", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
// all digits with leading binary prefix
{ "0b0", { 1, 0, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 176, 3 }, { 1, 0, 3 }, },
{ "0b1", { 1, 1, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 177, 3 }, { 1, 1, 3 }, },
{ "0b2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 178, 3 }, { 1, 0, 1 }, },
{ "0b3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 179, 3 }, { 1, 0, 1 }, },
{ "0b4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 180, 3 }, { 1, 0, 1 }, },
{ "0b5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 181, 3 }, { 1, 0, 1 }, },
{ "0b6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 182, 3 }, { 1, 0, 1 }, },
{ "0b7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 183, 3 }, { 1, 0, 1 }, },
{ "0b8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 184, 3 }, { 1, 0, 1 }, },
{ "0b9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 185, 3 }, { 1, 0, 1 }, },
{ "0bA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
{ "0bB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
{ "0bC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
{ "0bD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
{ "0bE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
{ "0bF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
{ "0bX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
{ "0ba", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
{ "0bb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
{ "0bc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
{ "0bd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
{ "0be", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
{ "0bf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
{ "0bx", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
// all digits with leading hexadecimal prefix
{ "0x0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 3 }, { 1, 0, 3 }, },
{ "0x1", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 3 }, { 1, 1, 3 }, },
{ "0x2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, },
{ "0x3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 3, 3 }, { 1, 3, 3 }, },
{ "0x4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 4, 3 }, { 1, 4, 3 }, },
{ "0x5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 5, 3 }, { 1, 5, 3 }, },
{ "0x6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 6, 3 }, { 1, 6, 3 }, },
{ "0x7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 7, 3 }, { 1, 7, 3 }, },
{ "0x8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 3 }, { 1, 8, 3 }, },
{ "0x9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 3 }, { 1, 9, 3 }, },
{ "0xA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
{ "0xB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
{ "0xC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
{ "0xD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
{ "0xE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
{ "0xF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
{ "0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
{ "0xa", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
{ "0xb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
{ "0xc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
{ "0xd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
{ "0xe", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
{ "0xf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
{ "0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
// terminator
{ "" }
};
#define SSCANF_TEST(string, format, expret, expval, explen) \
do { \
int ret = 0, val = 0, len = 0; \
ret = sscanf(string, format "%n", &val, &len); \
ATF_CHECK_EQ(expret, ret); \
if (expret && ret) { \
ATF_CHECK_EQ(expval, val); \
ATF_CHECK_EQ(explen, len); \
} \
} while (0)
ATF_TC_WITHOUT_HEAD(sscanf_b);
ATF_TC_BODY(sscanf_b, tc)
{
const struct sscanf_test_case *stc;
char input[16];
for (stc = sscanf_test_cases; *stc->input; stc++) {
strcpy(input + 1, stc->input);
SSCANF_TEST(input + 1, "%b", stc->b.ret, stc->b.val, stc->b.len);
input[0] = '+';
SSCANF_TEST(input, "%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
input[0] = '-';
SSCANF_TEST(input, "%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(sscanf_o);
ATF_TC_BODY(sscanf_o, tc)
{
const struct sscanf_test_case *stc;
char input[16];
for (stc = sscanf_test_cases; *stc->input; stc++) {
strcpy(input + 1, stc->input);
SSCANF_TEST(input + 1, "%o", stc->o.ret, stc->o.val, stc->o.len);
input[0] = '+';
SSCANF_TEST(input, "%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
input[0] = '-';
SSCANF_TEST(input, "%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(sscanf_d);
ATF_TC_BODY(sscanf_d, tc)
{
const struct sscanf_test_case *stc;
char input[16];
for (stc = sscanf_test_cases; *stc->input; stc++) {
strcpy(input + 1, stc->input);
SSCANF_TEST(input + 1, "%d", stc->d.ret, stc->d.val, stc->d.len);
input[0] = '+';
SSCANF_TEST(input, "%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
input[0] = '-';
SSCANF_TEST(input, "%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(sscanf_x);
ATF_TC_BODY(sscanf_x, tc)
{
const struct sscanf_test_case *stc;
char input[16];
for (stc = sscanf_test_cases; *stc->input; stc++) {
strcpy(input + 1, stc->input);
SSCANF_TEST(input + 1, "%x", stc->x.ret, stc->x.val, stc->x.len);
input[0] = '+';
SSCANF_TEST(input, "%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
input[0] = '-';
SSCANF_TEST(input, "%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(sscanf_i);
ATF_TC_BODY(sscanf_i, tc)
{
const struct sscanf_test_case *stc;
char input[16];
for (stc = sscanf_test_cases; *stc->input; stc++) {
strcpy(input + 1, stc->input);
SSCANF_TEST(input + 1, "%i", stc->i.ret, stc->i.val, stc->i.len);
input[0] = '+';
SSCANF_TEST(input, "%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
input[0] = '-';
SSCANF_TEST(input, "%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
}
}
/*
* Test termination cases: non-numeric character, fixed width, EOF
*/
ATF_TC_WITHOUT_HEAD(sscanf_termination);
ATF_TC_BODY(sscanf_termination, tc)
{
int a = 0, b = 0, c = 0;
char d = 0;
ATF_CHECK_EQ(4, sscanf("3.1415", "%d%c%2d%d", &a, &d, &b, &c));
ATF_CHECK_EQ(3, a);
ATF_CHECK_EQ(14, b);
ATF_CHECK_EQ(15, c);
ATF_CHECK_EQ('.', d);
}
ATF_TP_ADD_TCS(tp)
{
setlocale(LC_NUMERIC, "en_US.UTF-8");
ATF_TP_ADD_TC(tp, sscanf_b);
ATF_TP_ADD_TC(tp, sscanf_o);
ATF_TP_ADD_TC(tp, sscanf_d);
ATF_TP_ADD_TC(tp, sscanf_x);
ATF_TP_ADD_TC(tp, sscanf_i);
ATF_TP_ADD_TC(tp, sscanf_termination);
return (atf_no_error());
}

View file

@ -0,0 +1,140 @@
/*-
* Copyright (c) 2023 Dag-Erling Smørgrav
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <limits.h>
#include <locale.h>
#include <stdint.h>
#include <stdio.h>
#include <wchar.h>
#include <atf-c.h>
#ifndef nitems
#define nitems(a) (sizeof(a) / sizeof(a[0]))
#endif
#define SWPRINTF_TEST(output, format, ...) \
do { \
wchar_t buf[256]; \
assert(wcslen(L##output) < nitems(buf)); \
int ret = swprintf(buf, nitems(buf), L##format, \
__VA_ARGS__); \
ATF_CHECK_EQ(wcslen(L##output), ret); \
if (ret > 0) { \
ATF_CHECK_EQ(0, wcscmp(L##output, buf)); \
} \
} while (0)
ATF_TC_WITHOUT_HEAD(swprintf_b);
ATF_TC_BODY(swprintf_b, tc)
{
SWPRINTF_TEST("0", "%b", 0);
SWPRINTF_TEST(" 0", "%12b", 0);
SWPRINTF_TEST("000000000000", "%012b", 0);
SWPRINTF_TEST("1", "%b", 1);
SWPRINTF_TEST(" 1", "%12b", 1);
SWPRINTF_TEST("000000000001", "%012b", 1);
SWPRINTF_TEST("1111111111111111111111111111111", "%b", INT_MAX);
SWPRINTF_TEST("0", "%#b", 0);
SWPRINTF_TEST(" 0", "%#12b", 0);
SWPRINTF_TEST("000000000000", "%#012b", 0);
SWPRINTF_TEST("0b1", "%#b", 1);
SWPRINTF_TEST(" 0b1", "%#12b", 1);
SWPRINTF_TEST("0b0000000001", "%#012b", 1);
SWPRINTF_TEST("0b1111111111111111111111111111111", "%#b", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(swprintf_B);
ATF_TC_BODY(swprintf_B, tc)
{
SWPRINTF_TEST("0", "%B", 0);
SWPRINTF_TEST(" 0", "%12B", 0);
SWPRINTF_TEST("000000000000", "%012B", 0);
SWPRINTF_TEST("1", "%B", 1);
SWPRINTF_TEST(" 1", "%12B", 1);
SWPRINTF_TEST("000000000001", "%012B", 1);
SWPRINTF_TEST("1111111111111111111111111111111", "%B", INT_MAX);
SWPRINTF_TEST("0", "%#B", 0);
SWPRINTF_TEST(" 0", "%#12B", 0);
SWPRINTF_TEST("000000000000", "%#012B", 0);
SWPRINTF_TEST("0B1", "%#B", 1);
SWPRINTF_TEST(" 0B1", "%#12B", 1);
SWPRINTF_TEST("0B0000000001", "%#012B", 1);
SWPRINTF_TEST("0B1111111111111111111111111111111", "%#B", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(swprintf_d);
ATF_TC_BODY(swprintf_d, tc)
{
SWPRINTF_TEST("0", "%d", 0);
SWPRINTF_TEST(" 0", "%12d", 0);
SWPRINTF_TEST("000000000000", "%012d", 0);
SWPRINTF_TEST("1", "%d", 1);
SWPRINTF_TEST(" 1", "%12d", 1);
SWPRINTF_TEST("000000000001", "%012d", 1);
SWPRINTF_TEST("2147483647", "%d", INT_MAX);
SWPRINTF_TEST(" 2147483647", "%12d", INT_MAX);
SWPRINTF_TEST("002147483647", "%012d", INT_MAX);
SWPRINTF_TEST("2,147,483,647", "%'d", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(swprintf_x);
ATF_TC_BODY(swprintf_x, tc)
{
SWPRINTF_TEST("0", "%x", 0);
SWPRINTF_TEST(" 0", "%12x", 0);
SWPRINTF_TEST("000000000000", "%012x", 0);
SWPRINTF_TEST("1", "%x", 1);
SWPRINTF_TEST(" 1", "%12x", 1);
SWPRINTF_TEST("000000000001", "%012x", 1);
SWPRINTF_TEST("7fffffff", "%x", INT_MAX);
SWPRINTF_TEST(" 7fffffff", "%12x", INT_MAX);
SWPRINTF_TEST("00007fffffff", "%012x", INT_MAX);
SWPRINTF_TEST("0", "%#x", 0);
SWPRINTF_TEST(" 0", "%#12x", 0);
SWPRINTF_TEST("000000000000", "%#012x", 0);
SWPRINTF_TEST("0x1", "%#x", 1);
SWPRINTF_TEST(" 0x1", "%#12x", 1);
SWPRINTF_TEST("0x0000000001", "%#012x", 1);
SWPRINTF_TEST("0x7fffffff", "%#x", INT_MAX);
SWPRINTF_TEST(" 0x7fffffff", "%#12x", INT_MAX);
SWPRINTF_TEST("0x007fffffff", "%#012x", INT_MAX);
}
ATF_TC_WITHOUT_HEAD(swprintf_X);
ATF_TC_BODY(swprintf_X, tc)
{
SWPRINTF_TEST("0", "%X", 0);
SWPRINTF_TEST(" 0", "%12X", 0);
SWPRINTF_TEST("000000000000", "%012X", 0);
SWPRINTF_TEST("1", "%X", 1);
SWPRINTF_TEST(" 1", "%12X", 1);
SWPRINTF_TEST("000000000001", "%012X", 1);
SWPRINTF_TEST("7FFFFFFF", "%X", INT_MAX);
SWPRINTF_TEST(" 7FFFFFFF", "%12X", INT_MAX);
SWPRINTF_TEST("00007FFFFFFF", "%012X", INT_MAX);
SWPRINTF_TEST("0", "%#X", 0);
SWPRINTF_TEST(" 0", "%#12X", 0);
SWPRINTF_TEST("000000000000", "%#012X", 0);
SWPRINTF_TEST("0X1", "%#X", 1);
SWPRINTF_TEST(" 0X1", "%#12X", 1);
SWPRINTF_TEST("0X0000000001", "%#012X", 1);
SWPRINTF_TEST("0X7FFFFFFF", "%#X", INT_MAX);
SWPRINTF_TEST(" 0X7FFFFFFF", "%#12X", INT_MAX);
SWPRINTF_TEST("0X007FFFFFFF", "%#012X", INT_MAX);
}
ATF_TP_ADD_TCS(tp)
{
setlocale(LC_NUMERIC, "en_US.UTF-8");
ATF_TP_ADD_TC(tp, swprintf_b);
ATF_TP_ADD_TC(tp, swprintf_B);
ATF_TP_ADD_TC(tp, swprintf_d);
ATF_TP_ADD_TC(tp, swprintf_x);
ATF_TP_ADD_TC(tp, swprintf_X);
return (atf_no_error());
}

View file

@ -0,0 +1,267 @@
/*-
* Copyright (c) 2023 Dag-Erling Smørgrav
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <locale.h>
#include <stdint.h>
#include <stdio.h>
#include <wchar.h>
#include <atf-c.h>
static const struct swscanf_test_case {
wchar_t input[8];
struct {
int ret, val, len;
} b, o, d, x, i;
} swscanf_test_cases[] = {
// input binary octal decimal hexadecimal automatic
// all digits
{ L"0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
{ L"1", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
{ L"2", { 0, 0, 0 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, },
{ L"3", { 0, 0, 0 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, },
{ L"4", { 0, 0, 0 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, },
{ L"5", { 0, 0, 0 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, },
{ L"6", { 0, 0, 0 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, },
{ L"7", { 0, 0, 0 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, },
{ L"8", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 8, 1 }, { 1, 8, 1 }, { 1, 8, 1 }, },
{ L"9", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 9, 1 }, { 1, 9, 1 }, { 1, 9, 1 }, },
{ L"A", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
{ L"B", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
{ L"C", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
{ L"D", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
{ L"E", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
{ L"F", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
{ L"X", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
{ L"a", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
{ L"b", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
{ L"c", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
{ L"d", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
{ L"e", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
{ L"f", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
{ L"x", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
// all digits with leading zero
{ L"00", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
{ L"01", { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, },
{ L"02", { 1, 0, 1 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, },
{ L"03", { 1, 0, 1 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, },
{ L"04", { 1, 0, 1 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, },
{ L"05", { 1, 0, 1 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, },
{ L"06", { 1, 0, 1 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, },
{ L"07", { 1, 0, 1 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, },
{ L"08", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 2 }, { 1, 8, 2 }, { 1, 0, 1 }, },
{ L"09", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 2 }, { 1, 9, 2 }, { 1, 0, 1 }, },
{ L"0A", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
{ L"0B", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
{ L"0C", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
{ L"0D", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
{ L"0E", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
{ L"0F", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
{ L"0X", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
{ L"0a", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
{ L"0b", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
{ L"0c", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
{ L"0d", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
{ L"0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
{ L"0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
{ L"0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
// all digits with leading one
{ L"10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, },
{ L"11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, },
{ L"12", { 1, 1, 1 }, { 1, 10, 2 }, { 1, 12, 2 }, { 1, 18, 2 }, { 1, 12, 2 }, },
{ L"13", { 1, 1, 1 }, { 1, 11, 2 }, { 1, 13, 2 }, { 1, 19, 2 }, { 1, 13, 2 }, },
{ L"14", { 1, 1, 1 }, { 1, 12, 2 }, { 1, 14, 2 }, { 1, 20, 2 }, { 1, 14, 2 }, },
{ L"15", { 1, 1, 1 }, { 1, 13, 2 }, { 1, 15, 2 }, { 1, 21, 2 }, { 1, 15, 2 }, },
{ L"16", { 1, 1, 1 }, { 1, 14, 2 }, { 1, 16, 2 }, { 1, 22, 2 }, { 1, 16, 2 }, },
{ L"17", { 1, 1, 1 }, { 1, 15, 2 }, { 1, 17, 2 }, { 1, 23, 2 }, { 1, 17, 2 }, },
{ L"18", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 18, 2 }, { 1, 24, 2 }, { 1, 18, 2 }, },
{ L"19", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 19, 2 }, { 1, 25, 2 }, { 1, 19, 2 }, },
{ L"1A", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
{ L"1B", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
{ L"1C", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
{ L"1D", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
{ L"1E", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
{ L"1F", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
{ L"1X", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
{ L"1a", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
{ L"1b", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
{ L"1c", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
{ L"1d", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
{ L"1e", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
{ L"1f", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
{ L"1x", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
// all digits with leading binary prefix
{ L"0b0", { 1, 0, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 176, 3 }, { 1, 0, 3 }, },
{ L"0b1", { 1, 1, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 177, 3 }, { 1, 1, 3 }, },
{ L"0b2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 178, 3 }, { 1, 0, 1 }, },
{ L"0b3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 179, 3 }, { 1, 0, 1 }, },
{ L"0b4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 180, 3 }, { 1, 0, 1 }, },
{ L"0b5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 181, 3 }, { 1, 0, 1 }, },
{ L"0b6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 182, 3 }, { 1, 0, 1 }, },
{ L"0b7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 183, 3 }, { 1, 0, 1 }, },
{ L"0b8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 184, 3 }, { 1, 0, 1 }, },
{ L"0b9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 185, 3 }, { 1, 0, 1 }, },
{ L"0bA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
{ L"0bB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
{ L"0bC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
{ L"0bD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
{ L"0bE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
{ L"0bF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
{ L"0bX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
{ L"0ba", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
{ L"0bb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
{ L"0bc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
{ L"0bd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
{ L"0be", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
{ L"0bf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
{ L"0bx", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
// all digits with leading hexadecimal prefix
{ L"0x0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 3 }, { 1, 0, 3 }, },
{ L"0x1", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 3 }, { 1, 1, 3 }, },
{ L"0x2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, },
{ L"0x3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 3, 3 }, { 1, 3, 3 }, },
{ L"0x4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 4, 3 }, { 1, 4, 3 }, },
{ L"0x5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 5, 3 }, { 1, 5, 3 }, },
{ L"0x6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 6, 3 }, { 1, 6, 3 }, },
{ L"0x7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 7, 3 }, { 1, 7, 3 }, },
{ L"0x8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 3 }, { 1, 8, 3 }, },
{ L"0x9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 3 }, { 1, 9, 3 }, },
{ L"0xA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
{ L"0xB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
{ L"0xC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
{ L"0xD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
{ L"0xE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
{ L"0xF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
{ L"0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
{ L"0xa", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
{ L"0xb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
{ L"0xc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
{ L"0xd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
{ L"0xe", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
{ L"0xf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
{ L"0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
// terminator
{ L"" }
};
#define SWSCANF_TEST(string, format, expret, expval, explen) \
do { \
int ret = 0, val = 0, len = 0; \
ret = swscanf(string, format "%n", &val, &len); \
ATF_CHECK_EQ(expret, ret); \
if (expret && ret) { \
ATF_CHECK_EQ(expval, val); \
ATF_CHECK_EQ(explen, len); \
} \
} while (0)
ATF_TC_WITHOUT_HEAD(swscanf_b);
ATF_TC_BODY(swscanf_b, tc)
{
const struct swscanf_test_case *stc;
wchar_t input[16];
for (stc = swscanf_test_cases; *stc->input; stc++) {
wcscpy(input + 1, stc->input);
SWSCANF_TEST(input + 1, L"%b", stc->b.ret, stc->b.val, stc->b.len);
input[0] = L'+';
SWSCANF_TEST(input, L"%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
input[0] = L'-';
SWSCANF_TEST(input, L"%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(swscanf_o);
ATF_TC_BODY(swscanf_o, tc)
{
const struct swscanf_test_case *stc;
wchar_t input[16];
for (stc = swscanf_test_cases; *stc->input; stc++) {
wcscpy(input + 1, stc->input);
SWSCANF_TEST(input + 1, L"%o", stc->o.ret, stc->o.val, stc->o.len);
input[0] = L'+';
SWSCANF_TEST(input, L"%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
input[0] = L'-';
SWSCANF_TEST(input, L"%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(swscanf_d);
ATF_TC_BODY(swscanf_d, tc)
{
const struct swscanf_test_case *stc;
wchar_t input[16];
for (stc = swscanf_test_cases; *stc->input; stc++) {
wcscpy(input + 1, stc->input);
SWSCANF_TEST(input + 1, L"%d", stc->d.ret, stc->d.val, stc->d.len);
input[0] = L'+';
SWSCANF_TEST(input, L"%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
input[0] = L'-';
SWSCANF_TEST(input, L"%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(swscanf_x);
ATF_TC_BODY(swscanf_x, tc)
{
const struct swscanf_test_case *stc;
wchar_t input[16];
for (stc = swscanf_test_cases; *stc->input; stc++) {
wcscpy(input + 1, stc->input);
SWSCANF_TEST(input + 1, L"%x", stc->x.ret, stc->x.val, stc->x.len);
input[0] = L'+';
SWSCANF_TEST(input, L"%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
input[0] = L'-';
SWSCANF_TEST(input, L"%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
}
}
ATF_TC_WITHOUT_HEAD(swscanf_i);
ATF_TC_BODY(swscanf_i, tc)
{
const struct swscanf_test_case *stc;
wchar_t input[16];
for (stc = swscanf_test_cases; *stc->input; stc++) {
wcscpy(input + 1, stc->input);
SWSCANF_TEST(input + 1, L"%i", stc->i.ret, stc->i.val, stc->i.len);
input[0] = L'+';
SWSCANF_TEST(input, L"%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
input[0] = L'-';
SWSCANF_TEST(input, L"%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
}
}
/*
* Test termination cases: non-numeric character, fixed width, EOF
*/
ATF_TC_WITHOUT_HEAD(swscanf_termination);
ATF_TC_BODY(swscanf_termination, tc)
{
int a = 0, b = 0, c = 0;
char d = 0;
ATF_CHECK_EQ(4, swscanf(L"3.1415", L"%d%c%2d%d", &a, &d, &b, &c));
ATF_CHECK_EQ(3, a);
ATF_CHECK_EQ(14, b);
ATF_CHECK_EQ(15, c);
ATF_CHECK_EQ(L'.', d);
}
ATF_TP_ADD_TCS(tp)
{
setlocale(LC_NUMERIC, "en_US.UTF-8");
ATF_TP_ADD_TC(tp, swscanf_b);
ATF_TP_ADD_TC(tp, swscanf_o);
ATF_TP_ADD_TC(tp, swscanf_d);
ATF_TP_ADD_TC(tp, swscanf_x);
ATF_TP_ADD_TC(tp, swscanf_i);
ATF_TP_ADD_TC(tp, swscanf_termination);
return (atf_no_error());
}