1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-10-24 13:33:08 +03:00

stdio-common: Convert macros across scanf input specifier tests

Convert 'compare_real', 'read_real', and 'verify_input' macros to
functions so as to improve readability and avoid pitfalls.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
Maciej W. Rozycki
2025-08-23 01:02:10 +01:00
parent a1e5ee13ab
commit aa4dbb2eeb
5 changed files with 411 additions and 399 deletions

View File

@@ -16,6 +16,7 @@
License along with the GNU C Library; if not, see License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */ <https://www.gnu.org/licenses/>. */
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <support/next_to_fault.h> #include <support/next_to_fault.h>
@@ -62,69 +63,69 @@ do \
} \ } \
while (0) while (0)
#define verify_input(f, val, count, errp) \ static bool
({ \ verify_input (char f, type_t val, long long count, int *errp)
__label__ out, skip; \ {
bool match = true; \ bool match = true;
int err = 0; \ int err = 0;
size_t i; \ size_t i;
int ch; \ int ch;
\
for (i = 0; i < count; i++) \ for (i = 0; i < count; i++)
{ \ {
ch = read_input (); \ ch = read_input ();
if (ch < 0) \ if (ch < 0)
{ \ {
err = ch; \ err = ch;
goto out; \ goto out;
} \ }
if (ch == ':' && val[i] == '\0' && f == 's') \ if (ch == ':' && val[i] == '\0' && f == 's')
goto skip; \ goto skip;
if (ch != val[i]) \ if (ch != val[i])
{ \ {
match = false; \ match = false;
goto out; \ goto out;
} \ }
} \ }
ch = read_input (); \ ch = read_input ();
if (ch < 0) \ if (ch < 0)
{ \ {
err = ch; \ err = ch;
goto out; \ goto out;
} \ }
\
skip: \ skip:
if (f != 'c' && val[i++] != '\0') \ if (f != 'c' && val[i++] != '\0')
{ \ {
err = OUTPUT_TERM; \ err = OUTPUT_TERM;
goto out; \ goto out;
} \ }
if (val[i] != '\xa5') \ if (val[i] != '\xa5')
{ \ {
err = OUTPUT_OVERRUN; \ err = OUTPUT_OVERRUN;
goto out; \ goto out;
} \ }
\
while (ch != ':') \ while (ch != ':')
{ \ {
ch = read_input (); \ ch = read_input ();
if (ch < 0) \ if (ch < 0)
{ \ {
err = ch; \ err = ch;
goto out; \ goto out;
} \ }
match = false; \ match = false;
} \ }
\
out: \ out:
if (err || !match) \ if (err || !match)
{ \ {
printf ("error: %s:%d: input buffer: `", __FILE__, __LINE__); \ printf ("error: %s:%d: input buffer: `", __FILE__, __LINE__);
for (size_t j = 0; j <= i; j++) \ for (size_t j = 0; j <= i; j++)
printf ("%c", val[j]); \ printf ("%c", val[j]);
printf ("'\n"); \ printf ("'\n");
} \ }
\
*errp = err; \ *errp = err;
match; \ return match;
}) }

View File

@@ -16,36 +16,45 @@
License along with the GNU C Library; if not, see License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */ <https://www.gnu.org/licenses/>. */
#include <stdbool.h>
#include <string.h> #include <string.h>
/* Reference data is a signed decimal integer constant to compare against /* Reference data is a signed decimal integer constant to compare against
arithmetically. */ arithmetically. */
/* Tweak our environment according to any TYPE_T_UNSIGNED_P setting
supplied by the individual test case. */
#ifdef TYPE_T_UNSIGNED_P
# define UNSIGNED unsigned
#else
# define UNSIGNED
#endif
#define pointer_to_value(val) (&(val)) #define pointer_to_value(val) (&(val))
#define initialize_value(val) \ #define initialize_value(val) \
memset (&val, 0xa5, sizeof (val)) memset (&val, 0xa5, sizeof (val))
#define verify_input(f, val, count, errp) \ static bool
({ \ verify_input (char f, type_t val, long long count, int *errp)
__label__ out; \ {
bool match = true; \ bool match = true;
int err; \ int err;
\
UNSIGNED long long v = read_integer (&err); \ UNSIGNED long long v = read_integer (&err);
if (err < 0) \ if (err < 0)
goto out; \ goto out;
match = val == v; \ match = val == v;
\
out: \ out:
if (err || !match) \ if (err || !match)
{ \ {
printf ("error: %s:%d: input: %016llx\n", \ printf ("error: %s:%d: input: %016llx\n",
__FILE__, __LINE__, (long long) val); \ __FILE__, __LINE__, (long long) val);
printf ("error: %s:%d: value: %016llx\n", \ printf ("error: %s:%d: value: %016llx\n",
__FILE__, __LINE__, v); \ __FILE__, __LINE__, v);
} \ }
\
*errp = err; \ *errp = err;
match; \ return match;
}) }

View File

@@ -66,301 +66,306 @@
#define initialize_value(val) \ #define initialize_value(val) \
memset (&val, 0xa5, sizeof (val)) memset (&val, 0xa5, sizeof (val))
#define compare_real(x, y) \ #ifndef compare_real
(memcmp (&(x), &(y), sizeof (y)) == 0) static bool
compare_real (type_t x, type_t y)
{
return memcmp (&x, &y, sizeof (y)) == 0;
}
#endif
#define verify_input(f, val, count, errp) \ static type_t
({ \ read_real (int *errp)
__label__ out; \ {
bool match = true; \ bool m = false;
int errx = 0; \ int err = 0;
type_t v; \ type_t v;
\ int ch;
initialize_value (v); \
/* Make sure it's been committed. */ \
__asm__ ("" : : : "memory"); \
\
v = read_real (&errx); \
if (errx < 0) \
goto out; \
\
match = compare_real (val, v); \
if (!match) \
{ \
union \
{ \
type_t v; \
unsigned char x[sizeof (type_t)]; \
} \
uv = { .v = v }, ui = { .v = val }; \
\
printf ("error: %s:%d: input buffer: `", __FILE__, __LINE__); \
for (size_t j = 0; j < sizeof (ui.x); j++) \
printf ("%02hhx", ui.x[j]); \
printf ("'\n"); \
printf ("error: %s:%d: value buffer: `", __FILE__, __LINE__); \
for (size_t j = 0; j < sizeof (uv.x); j++) \
printf ("%02hhx", uv.x[j]); \
printf ("'\n"); \
} \
\
out: \
*errp = errx; \
match; \
})
#define read_real(errp) \ ch = read_input ();
({ \ if (ch == '-' || ch == '+')
__label__ out; \ {
bool m = false; \ m = ch == '-';
int err = 0; \ ch = read_input ();
type_t v; \ }
int ch; \
\ switch (ch)
ch = read_input (); \ {
if (ch == '-' || ch == '+') \ case '0':
{ \ break;
m = ch == '-'; \ case 'I':
ch = read_input (); \ case 'i':
} \ {
\ static const char unf[] = { 'N', 'F' };
switch (ch) \ static const char lnf[] = { 'n', 'f' };
{ \ size_t i;
case '0': \
break; \ for (i = 0; i < sizeof (unf); i++)
case 'I': \ {
case 'i': \ ch = read_input ();
{ \ if (ch != unf[i] && ch != lnf[i])
static const char unf[] = { 'N', 'F' }; \ {
static const char lnf[] = { 'n', 'f' }; \ err = ch < 0 ? ch : INPUT_FORMAT;
size_t i; \ v = NAN;
\ goto out;
for (i = 0; i < sizeof (unf); i++) \ }
{ \ }
ch = read_input (); \
if (ch != unf[i] && ch != lnf[i]) \ ch = read_input ();
{ \ if (ch == ':')
err = ch < 0 ? ch : INPUT_FORMAT; \ {
v = NAN; \ v = m ? -INFINITY : +INFINITY;
goto out; \ goto out;
} \ }
} \
\ static const char uinity[] = { 'I', 'N', 'I', 'T', 'Y' };
ch = read_input (); \ static const char linity[] = { 'i', 'n', 'i', 't', 'y' };
if (ch == ':') \
{ \ for (i = 0; i < sizeof (uinity); i++)
v = m ? -INFINITY : +INFINITY; \ {
goto out; \ if (ch != uinity[i] && ch != linity[i])
} \ {
\ err = ch < 0 ? ch : INPUT_FORMAT;
static const char uinity[] = { 'I', 'N', 'I', 'T', 'Y' }; \ v = NAN;
static const char linity[] = { 'i', 'n', 'i', 't', 'y' }; \ goto out;
\ }
for (i = 0; i < sizeof (uinity); i++) \ ch = read_input ();
{ \ }
if (ch != uinity[i] && ch != linity[i]) \ if (ch == ':')
{ \ {
err = ch < 0 ? ch : INPUT_FORMAT; \ v = m ? -INFINITY : +INFINITY;
v = NAN; \ goto out;
goto out; \ }
} \ }
ch = read_input (); \ err = ch < 0 ? ch : INPUT_FORMAT;
} \ v = NAN;
if (ch == ':') \ goto out;
{ \
v = m ? -INFINITY : +INFINITY; \ case 'N':
goto out; \ case 'n':
} \ {
} \ static const char uan[] = { 'A', 'N' };
err = ch < 0 ? ch : INPUT_FORMAT; \ static const char lan[] = { 'a', 'n' };
v = NAN; \ size_t i;
goto out; \
\ for (i = 0; i < sizeof (uan); i++)
case 'N': \ {
case 'n': \ ch = read_input ();
{ \ if (ch != uan[i] && ch != lan[i])
static const char uan[] = { 'A', 'N' }; \ {
static const char lan[] = { 'a', 'n' }; \ err = ch < 0 ? ch : INPUT_FORMAT;
size_t i; \ v = NAN;
\ goto out;
for (i = 0; i < sizeof (uan); i++) \ }
{ \ }
ch = read_input (); \
if (ch != uan[i] && ch != lan[i]) \ ch = read_input ();
{ \ if (ch == ':')
err = ch < 0 ? ch : INPUT_FORMAT; \ {
v = NAN; \ v = m ? -nan (v, ".") : nan (v, ".");
goto out; \ goto out;
} \ }
} \
\ if (ch == '(')
ch = read_input (); \ {
if (ch == ':') \ size_t seq_size = 0;
{ \ char *seq = NULL;
v = m ? -nan (v, ".") : nan (v, "."); \ i = 0;
goto out; \ while (1)
} \ {
\ if (i == seq_size)
if (ch == '(') \ {
{ \ seq_size += SIZE_CHUNK;
size_t seq_size = 0; \ seq = xrealloc (seq, seq_size);
char *seq = NULL; \ }
i = 0; \ ch = read_input ();
while (1) \ if (ch == ')')
{ \ break;
if (i == seq_size) \ if (ch != '_' && !isdigit (ch)
{ \ && !(ch >= 'A' && ch <= 'Z')
seq_size += SIZE_CHUNK; \ && !(ch >= 'a' && ch <= 'z'))
seq = xrealloc (seq, seq_size); \ {
} \ free (seq);
ch = read_input (); \ err = ch < 0 ? ch : INPUT_FORMAT;
if (ch == ')') \ v = NAN;
break; \ goto out;
if (ch != '_' && !isdigit (ch) \ }
&& !(ch >= 'A' && ch <= 'Z') \ seq[i++] = ch;
&& !(ch >= 'a' && ch <= 'z')) \ }
{ \ seq[i] = '\0';
free (seq); \
err = ch < 0 ? ch : INPUT_FORMAT; \ ch = read_input ();
v = NAN; \ if (ch == ':')
goto out; \ {
} \ v = m ? -nan (v, seq) : nan (v, seq);
seq[i++] = ch; \ free (seq);
} \ goto out;
seq[i] = '\0'; \ }
\ free (seq);
ch = read_input (); \ }
if (ch == ':') \ }
{ \ err = ch < 0 ? ch : INPUT_FORMAT;
v = m ? -nan (v, seq) : nan (v, seq); \ v = NAN;
free (seq); \ goto out;
goto out; \
} \ default:
free (seq); \ err = ch < 0 ? ch : INPUT_FORMAT;
} \ v = NAN;
} \ goto out;
err = ch < 0 ? ch : INPUT_FORMAT; \ }
v = NAN; \
goto out; \ ch = read_input ();
\ if (ch != 'X' && ch != 'x')
default: \ {
err = ch < 0 ? ch : INPUT_FORMAT; \ err = ch < 0 ? ch : INPUT_FORMAT;
v = NAN; \ v = NAN;
goto out; \ goto out;
} \ }
\
ch = read_input (); \ type_t f = m ? -1.0 : 1.0;
if (ch != 'X' && ch != 'x') \ v = m ? -0.0 : 0.0;
{ \ int i = 0;
err = ch < 0 ? ch : INPUT_FORMAT; \ do
v = NAN; \ {
goto out; \ int d = 0;
} \
\ ch = read_input ();
type_t f = m ? -1.0 : 1.0; \
v = m ? -0.0 : 0.0; \ if (i == 1)
int i = 0; \ switch (ch)
do \ {
{ \ case '.':
int d = 0; \ i++;
\ continue;
ch = read_input (); \
\ case ':':
if (i == 1) \ case 'P':
switch (ch) \ case 'p':
{ \ break;
case '.': \
i++; \ default:
continue; \ err = ch < 0 ? ch : INPUT_FORMAT;
\ v = NAN;
case ':': \ goto out;
case 'P': \ }
case 'p': \
break; \ switch (ch)
\ {
default: \ case '0':
err = ch < 0 ? ch : INPUT_FORMAT; \ case '1':
v = NAN; \ case '2':
goto out; \ case '3':
} \ case '4':
\ case '5':
switch (ch) \ case '6':
{ \ case '7':
case '0': \ case '8':
case '1': \ case '9':
case '2': \ d = ch - '0';
case '3': \ break;
case '4': \
case '5': \ case 'A':
case '6': \ case 'B':
case '7': \ case 'C':
case '8': \ case 'D':
case '9': \ case 'E':
d = ch - '0'; \ case 'F':
break; \ d = ch - 'A' + 10;
\ break;
case 'A': \
case 'B': \ case 'a':
case 'C': \ case 'b':
case 'D': \ case 'c':
case 'E': \ case 'd':
case 'F': \ case 'e':
d = ch - 'A' + 10; \ case 'f':
break; \ d = ch - 'a' + 10;
\ break;
case 'a': \
case 'b': \ case ':':
case 'c': \ case 'P':
case 'd': \ case 'p':
case 'e': \ if (i == 0)
case 'f': \ {
d = ch - 'a' + 10; \ err = INPUT_FORMAT;
break; \ v = NAN;
\ goto out;
case ':': \ }
case 'P': \ break;
case 'p': \
if (i == 0) \ default:
{ \ err = ch < 0 ? ch : INPUT_FORMAT;
err = INPUT_FORMAT; \ v = NAN;
v = NAN; \ goto out;
goto out; \ }
} \
break; \ v += f * d;
\ f /= 16.0l;
default: \ i++;
err = ch < 0 ? ch : INPUT_FORMAT; \ }
v = NAN; \ while (ch != ':' && ch != 'P' && ch != 'p');
goto out; \
} \ long long exp = 0;
\ if (ch == 'P' || ch == 'p')
v += f * d; \ {
f /= 16.0l; \ exp = read_integer (&err);
i++; \ if (err)
} \ {
while (ch != ':' && ch != 'P' && ch != 'p'); \ v = NAN;
\ goto out;
long long exp = 0; \ }
if (ch == 'P' || ch == 'p') \ }
{ \
exp = read_integer (&err); \ errno = 0;
if (err) \ v = ldexp (v, exp);
{ \ if ((v == HUGE_VALL || v == -HUGE_VALL) && errno != 0)
v = NAN; \ {
goto out; \ err = INPUT_OVERFLOW;
} \ v = NAN;
} \ goto out;
\ }
errno = 0; \
v = ldexp (v, exp); \ out:
if ((v == HUGE_VALL || v == -HUGE_VALL) && errno != 0) \ *errp = err;
{ \ return v;
err = INPUT_OVERFLOW; \ }
v = NAN; \
goto out; \ static bool
} \ verify_input (char f, type_t val, long long count, int *errp)
\ {
out: \ bool match = true;
*errp = err; \ int err = 0;
v; \ type_t v;
})
initialize_value (v);
/* Make sure it's been committed. */
__asm__ ("" : : : "memory");
v = read_real (&err);
if (err < 0)
goto out;
match = compare_real (val, v);
if (!match)
{
union
{
type_t v;
unsigned char x[sizeof (type_t)];
}
uv = { .v = v }, ui = { .v = val };
printf ("error: %s:%d: input buffer: `", __FILE__, __LINE__);
for (size_t j = 0; j < sizeof (ui.x); j++)
printf ("%02hhx", ui.x[j]);
printf ("'\n");
printf ("error: %s:%d: value buffer: `", __FILE__, __LINE__);
for (size_t j = 0; j < sizeof (uv.x); j++)
printf ("%02hhx", uv.x[j]);
printf ("'\n");
}
out:
*errp = err;
return match;
}

View File

@@ -60,11 +60,6 @@
#ifndef TYPE_T_UNSIGNED_P #ifndef TYPE_T_UNSIGNED_P
# define TYPE_T_UNSIGNED_P 0 # define TYPE_T_UNSIGNED_P 0
#endif #endif
#if TYPE_T_UNSIGNED_P
# define UNSIGNED unsigned
#else
# define UNSIGNED
#endif
/* Read and return a single character from standard input, returning /* Read and return a single character from standard input, returning
end-of-file or error status indication where applicable. */ end-of-file or error status indication where applicable. */

View File

@@ -17,19 +17,21 @@
<https://www.gnu.org/licenses/>. */ <https://www.gnu.org/licenses/>. */
#include <math_ldbl.h> #include <math_ldbl.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
typedef long double type_t; typedef long double type_t;
static bool
compare_real (type_t x, type_t y)
{
ieee_long_double_shape_type ux = { .value = x }, uy = { .value = y };
return (ux.parts.lsw == uy.parts.lsw && ux.parts.msw == uy.parts.msw
&& ux.parts.sign_exponent == uy.parts.sign_exponent);
}
#define compare_real compare_real
#include "tst-scanf-format-real.h" #include "tst-scanf-format-real.h"
#undef compare_real
#define compare_real(x, y) \
({ \
ieee_long_double_shape_type ux = { .value = x }, uy = { .value = y }; \
(ux.parts.lsw == uy.parts.lsw && ux.parts.msw == uy.parts.msw \
&& ux.parts.sign_exponent == uy.parts.sign_exponent); \
})
#include "tst-scanf-format-skeleton.c" #include "tst-scanf-format-skeleton.c"