Austin Schuh | dace2a6 | 2020-08-18 10:56:48 -0700 | [diff] [blame^] | 1 | /* Test gmp_printf and related functions. |
| 2 | |
| 3 | Copyright 2001-2003, 2015 Free Software Foundation, Inc. |
| 4 | |
| 5 | This file is part of the GNU MP Library test suite. |
| 6 | |
| 7 | The GNU MP Library test suite is free software; you can redistribute it |
| 8 | and/or modify it under the terms of the GNU General Public License as |
| 9 | published by the Free Software Foundation; either version 3 of the License, |
| 10 | or (at your option) any later version. |
| 11 | |
| 12 | The GNU MP Library test suite is distributed in the hope that it will be |
| 13 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
| 15 | Public License for more details. |
| 16 | |
| 17 | You should have received a copy of the GNU General Public License along with |
| 18 | the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ |
| 19 | |
| 20 | |
| 21 | /* Usage: t-printf [-s] |
| 22 | |
| 23 | -s Check the data against the system printf, where possible. This is |
| 24 | only an option since we don't want to fail if the system printf is |
| 25 | faulty or strange. */ |
| 26 | |
| 27 | |
| 28 | #include "config.h" /* needed for the HAVE_, could also move gmp incls */ |
| 29 | |
| 30 | #include <stdarg.h> |
| 31 | #include <stddef.h> /* for ptrdiff_t */ |
| 32 | #include <stdio.h> |
| 33 | #include <stdlib.h> |
| 34 | #include <string.h> |
| 35 | |
| 36 | #if HAVE_OBSTACK_VPRINTF |
| 37 | #define obstack_chunk_alloc tests_allocate |
| 38 | #define obstack_chunk_free tests_free_nosize |
| 39 | #include <obstack.h> |
| 40 | #endif |
| 41 | |
| 42 | #if HAVE_INTTYPES_H |
| 43 | # include <inttypes.h> /* for intmax_t */ |
| 44 | #else |
| 45 | # if HAVE_STDINT_H |
| 46 | # include <stdint.h> |
| 47 | # endif |
| 48 | #endif |
| 49 | |
| 50 | #if HAVE_UNISTD_H |
| 51 | #include <unistd.h> /* for unlink */ |
| 52 | #endif |
| 53 | |
| 54 | #include "gmp-impl.h" |
| 55 | #include "tests.h" |
| 56 | |
| 57 | |
| 58 | int option_check_printf = 0; |
| 59 | |
| 60 | |
| 61 | #define CHECK_VFPRINTF_FILENAME "t-printf.tmp" |
| 62 | FILE *check_vfprintf_fp; |
| 63 | |
| 64 | |
| 65 | /* From any of the tests run here. */ |
| 66 | #define MAX_OUTPUT 1024 |
| 67 | |
| 68 | |
| 69 | void |
| 70 | check_plain (const char *want, const char *fmt_orig, ...) |
| 71 | { |
| 72 | char got[MAX_OUTPUT]; |
| 73 | int got_len, want_len; |
| 74 | size_t fmtsize; |
| 75 | char *fmt, *q; |
| 76 | const char *p; |
| 77 | va_list ap; |
| 78 | va_start (ap, fmt_orig); |
| 79 | |
| 80 | if (! option_check_printf) |
| 81 | return; |
| 82 | |
| 83 | fmtsize = strlen (fmt_orig) + 1; |
| 84 | fmt = (char *) (*__gmp_allocate_func) (fmtsize); |
| 85 | |
| 86 | for (p = fmt_orig, q = fmt; *p != '\0'; p++) |
| 87 | { |
| 88 | switch (*p) { |
| 89 | case 'a': |
| 90 | case 'A': |
| 91 | /* The exact value of the exponent isn't guaranteed in glibc, and it |
| 92 | and gmp_printf do slightly different things, so don't compare |
| 93 | directly. */ |
| 94 | goto done; |
| 95 | case 'F': |
| 96 | if (p > fmt_orig && *(p-1) == '.') |
| 97 | goto done; /* don't test the "all digits" cases */ |
| 98 | /* discard 'F' type */ |
| 99 | break; |
| 100 | case 'Z': |
| 101 | /* transmute */ |
| 102 | *q++ = 'l'; |
| 103 | break; |
| 104 | default: |
| 105 | *q++ = *p; |
| 106 | break; |
| 107 | } |
| 108 | } |
| 109 | *q = '\0'; |
| 110 | |
| 111 | want_len = strlen (want); |
| 112 | ASSERT_ALWAYS (want_len < sizeof(got)); |
| 113 | |
| 114 | got_len = vsprintf (got, fmt, ap); |
| 115 | |
| 116 | if (got_len != want_len || strcmp (got, want) != 0) |
| 117 | { |
| 118 | printf ("wanted data doesn't match plain vsprintf\n"); |
| 119 | printf (" fmt |%s|\n", fmt); |
| 120 | printf (" got |%s|\n", got); |
| 121 | printf (" want |%s|\n", want); |
| 122 | printf (" got_len %d\n", got_len); |
| 123 | printf (" want_len %d\n", want_len); |
| 124 | abort (); |
| 125 | } |
| 126 | |
| 127 | done: |
| 128 | (*__gmp_free_func) (fmt, fmtsize); |
| 129 | } |
| 130 | |
| 131 | void |
| 132 | check_vsprintf (const char *want, const char *fmt, va_list ap) |
| 133 | { |
| 134 | char got[MAX_OUTPUT]; |
| 135 | int got_len, want_len; |
| 136 | |
| 137 | want_len = strlen (want); |
| 138 | got_len = gmp_vsprintf (got, fmt, ap); |
| 139 | |
| 140 | if (got_len != want_len || strcmp (got, want) != 0) |
| 141 | { |
| 142 | printf ("gmp_vsprintf wrong\n"); |
| 143 | printf (" fmt |%s|\n", fmt); |
| 144 | printf (" got |%s|\n", got); |
| 145 | printf (" want |%s|\n", want); |
| 146 | printf (" got_len %d\n", got_len); |
| 147 | printf (" want_len %d\n", want_len); |
| 148 | abort (); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | void |
| 153 | check_vfprintf (const char *want, const char *fmt, va_list ap) |
| 154 | { |
| 155 | char got[MAX_OUTPUT]; |
| 156 | int got_len, want_len, fread_len; |
| 157 | long ftell_len; |
| 158 | |
| 159 | want_len = strlen (want); |
| 160 | |
| 161 | rewind (check_vfprintf_fp); |
| 162 | got_len = gmp_vfprintf (check_vfprintf_fp, fmt, ap); |
| 163 | ASSERT_ALWAYS (got_len != -1); |
| 164 | ASSERT_ALWAYS (fflush (check_vfprintf_fp) == 0); |
| 165 | |
| 166 | ftell_len = ftell (check_vfprintf_fp); |
| 167 | ASSERT_ALWAYS (ftell_len != -1); |
| 168 | |
| 169 | rewind (check_vfprintf_fp); |
| 170 | ASSERT_ALWAYS (ftell_len <= sizeof(got)); |
| 171 | fread_len = fread (got, 1, ftell_len, check_vfprintf_fp); |
| 172 | |
| 173 | if (got_len != want_len |
| 174 | || ftell_len != want_len |
| 175 | || fread_len != want_len |
| 176 | || memcmp (got, want, want_len) != 0) |
| 177 | { |
| 178 | printf ("gmp_vfprintf wrong\n"); |
| 179 | printf (" fmt |%s|\n", fmt); |
| 180 | printf (" got |%.*s|\n", fread_len, got); |
| 181 | printf (" want |%s|\n", want); |
| 182 | printf (" got_len %d\n", got_len); |
| 183 | printf (" ftell_len %ld\n", ftell_len); |
| 184 | printf (" fread_len %d\n", fread_len); |
| 185 | printf (" want_len %d\n", want_len); |
| 186 | abort (); |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | void |
| 191 | check_vsnprintf (const char *want, const char *fmt, va_list ap) |
| 192 | { |
| 193 | char got[MAX_OUTPUT+1]; |
| 194 | int ret, got_len, want_len; |
| 195 | size_t bufsize; |
| 196 | |
| 197 | want_len = strlen (want); |
| 198 | |
| 199 | bufsize = -1; |
| 200 | for (;;) |
| 201 | { |
| 202 | /* do 0 to 5, then want-5 to want+5 */ |
| 203 | bufsize++; |
| 204 | if (bufsize > 5 && bufsize < want_len-5) |
| 205 | bufsize = want_len-5; |
| 206 | if (bufsize > want_len + 5) |
| 207 | break; |
| 208 | ASSERT_ALWAYS (bufsize+1 <= sizeof (got)); |
| 209 | |
| 210 | got[bufsize] = '!'; |
| 211 | ret = gmp_vsnprintf (got, bufsize, fmt, ap); |
| 212 | |
| 213 | got_len = MIN (MAX(1,bufsize)-1, want_len); |
| 214 | |
| 215 | if (got[bufsize] != '!') |
| 216 | { |
| 217 | printf ("gmp_vsnprintf overwrote bufsize sentinel\n"); |
| 218 | goto error; |
| 219 | } |
| 220 | |
| 221 | if (ret != want_len) |
| 222 | { |
| 223 | printf ("gmp_vsnprintf return value wrong\n"); |
| 224 | goto error; |
| 225 | } |
| 226 | |
| 227 | if (bufsize > 0) |
| 228 | { |
| 229 | if (memcmp (got, want, got_len) != 0 || got[got_len] != '\0') |
| 230 | { |
| 231 | printf ("gmp_vsnprintf wrong result string\n"); |
| 232 | error: |
| 233 | printf (" fmt |%s|\n", fmt); |
| 234 | printf (" bufsize %lu\n", (unsigned long) bufsize); |
| 235 | printf (" got |%s|\n", got); |
| 236 | printf (" want |%.*s|\n", got_len, want); |
| 237 | printf (" want full |%s|\n", want); |
| 238 | printf (" ret %d\n", ret); |
| 239 | printf (" want_len %d\n", want_len); |
| 240 | abort (); |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | void |
| 247 | check_vasprintf (const char *want, const char *fmt, va_list ap) |
| 248 | { |
| 249 | char *got; |
| 250 | int got_len, want_len; |
| 251 | |
| 252 | want_len = strlen (want); |
| 253 | got_len = gmp_vasprintf (&got, fmt, ap); |
| 254 | |
| 255 | if (got_len != want_len || strcmp (got, want) != 0) |
| 256 | { |
| 257 | printf ("gmp_vasprintf wrong\n"); |
| 258 | printf (" fmt |%s|\n", fmt); |
| 259 | printf (" got |%s|\n", got); |
| 260 | printf (" want |%s|\n", want); |
| 261 | printf (" got_len %d\n", got_len); |
| 262 | printf (" want_len %d\n", want_len); |
| 263 | abort (); |
| 264 | } |
| 265 | (*__gmp_free_func) (got, strlen(got)+1); |
| 266 | } |
| 267 | |
| 268 | void |
| 269 | check_obstack_vprintf (const char *want, const char *fmt, va_list ap) |
| 270 | { |
| 271 | #if HAVE_OBSTACK_VPRINTF |
| 272 | struct obstack ob; |
| 273 | int got_len, want_len, ob_len; |
| 274 | char *got; |
| 275 | |
| 276 | want_len = strlen (want); |
| 277 | |
| 278 | obstack_init (&ob); |
| 279 | got_len = gmp_obstack_vprintf (&ob, fmt, ap); |
| 280 | got = (char *) obstack_base (&ob); |
| 281 | ob_len = obstack_object_size (&ob); |
| 282 | |
| 283 | if (got_len != want_len |
| 284 | || ob_len != want_len |
| 285 | || memcmp (got, want, want_len) != 0) |
| 286 | { |
| 287 | printf ("gmp_obstack_vprintf wrong\n"); |
| 288 | printf (" fmt |%s|\n", fmt); |
| 289 | printf (" got |%s|\n", got); |
| 290 | printf (" want |%s|\n", want); |
| 291 | printf (" got_len %d\n", got_len); |
| 292 | printf (" ob_len %d\n", ob_len); |
| 293 | printf (" want_len %d\n", want_len); |
| 294 | abort (); |
| 295 | } |
| 296 | obstack_free (&ob, NULL); |
| 297 | #endif |
| 298 | } |
| 299 | |
| 300 | |
| 301 | void |
| 302 | check_one (const char *want, const char *fmt, ...) |
| 303 | { |
| 304 | va_list ap; |
| 305 | va_start (ap, fmt); |
| 306 | |
| 307 | /* simplest first */ |
| 308 | check_vsprintf (want, fmt, ap); |
| 309 | check_vfprintf (want, fmt, ap); |
| 310 | check_vsnprintf (want, fmt, ap); |
| 311 | check_vasprintf (want, fmt, ap); |
| 312 | check_obstack_vprintf (want, fmt, ap); |
| 313 | } |
| 314 | |
| 315 | |
| 316 | #define hex_or_octal_p(fmt) \ |
| 317 | (strchr (fmt, 'x') != NULL \ |
| 318 | || strchr (fmt, 'X') != NULL \ |
| 319 | || strchr (fmt, 'o') != NULL) |
| 320 | |
| 321 | void |
| 322 | check_z (void) |
| 323 | { |
| 324 | static const struct { |
| 325 | const char *fmt; |
| 326 | const char *z; |
| 327 | const char *want; |
| 328 | } data[] = { |
| 329 | { "%Zd", "0", "0" }, |
| 330 | { "%Zd", "1", "1" }, |
| 331 | { "%Zd", "123", "123" }, |
| 332 | { "%Zd", "-1", "-1" }, |
| 333 | { "%Zd", "-123", "-123" }, |
| 334 | |
| 335 | { "%+Zd", "0", "+0" }, |
| 336 | { "%+Zd", "123", "+123" }, |
| 337 | { "%+Zd", "-123", "-123" }, |
| 338 | |
| 339 | { "%Zx", "123", "7b" }, |
| 340 | { "%ZX", "123", "7B" }, |
| 341 | { "%Zx", "-123", "-7b" }, |
| 342 | { "%ZX", "-123", "-7B" }, |
| 343 | { "%Zo", "123", "173" }, |
| 344 | { "%Zo", "-123", "-173" }, |
| 345 | |
| 346 | { "%#Zx", "0", "0" }, |
| 347 | { "%#ZX", "0", "0" }, |
| 348 | { "%#Zx", "123", "0x7b" }, |
| 349 | { "%#ZX", "123", "0X7B" }, |
| 350 | { "%#Zx", "-123", "-0x7b" }, |
| 351 | { "%#ZX", "-123", "-0X7B" }, |
| 352 | |
| 353 | { "%#Zo", "0", "0" }, |
| 354 | { "%#Zo", "123", "0173" }, |
| 355 | { "%#Zo", "-123", "-0173" }, |
| 356 | |
| 357 | { "%10Zd", "0", " 0" }, |
| 358 | { "%10Zd", "123", " 123" }, |
| 359 | { "%10Zd", "-123", " -123" }, |
| 360 | |
| 361 | { "%-10Zd", "0", "0 " }, |
| 362 | { "%-10Zd", "123", "123 " }, |
| 363 | { "%-10Zd", "-123", "-123 " }, |
| 364 | |
| 365 | { "%+10Zd", "123", " +123" }, |
| 366 | { "%+-10Zd", "123", "+123 " }, |
| 367 | { "%+10Zd", "-123", " -123" }, |
| 368 | { "%+-10Zd", "-123", "-123 " }, |
| 369 | |
| 370 | { "%08Zd", "0", "00000000" }, |
| 371 | { "%08Zd", "123", "00000123" }, |
| 372 | { "%08Zd", "-123", "-0000123" }, |
| 373 | |
| 374 | { "%+08Zd", "0", "+0000000" }, |
| 375 | { "%+08Zd", "123", "+0000123" }, |
| 376 | { "%+08Zd", "-123", "-0000123" }, |
| 377 | |
| 378 | { "%#08Zx", "0", "00000000" }, |
| 379 | { "%#08Zx", "123", "0x00007b" }, |
| 380 | { "%#08Zx", "-123", "-0x0007b" }, |
| 381 | |
| 382 | { "%+#08Zx", "0", "+0000000" }, |
| 383 | { "%+#08Zx", "123", "+0x0007b" }, |
| 384 | { "%+#08Zx", "-123", "-0x0007b" }, |
| 385 | |
| 386 | { "%.0Zd", "0", "" }, |
| 387 | { "%.1Zd", "0", "0" }, |
| 388 | { "%.2Zd", "0", "00" }, |
| 389 | { "%.3Zd", "0", "000" }, |
| 390 | }; |
| 391 | |
| 392 | int i, j; |
| 393 | mpz_t z; |
| 394 | char *nfmt; |
| 395 | mp_size_t nsize, zeros; |
| 396 | |
| 397 | mpz_init (z); |
| 398 | |
| 399 | for (i = 0; i < numberof (data); i++) |
| 400 | { |
| 401 | mpz_set_str_or_abort (z, data[i].z, 0); |
| 402 | |
| 403 | /* don't try negatives or forced sign in hex or octal */ |
| 404 | if (mpz_fits_slong_p (z) |
| 405 | && ! (hex_or_octal_p (data[i].fmt) |
| 406 | && (strchr (data[i].fmt, '+') != NULL || mpz_sgn(z) < 0))) |
| 407 | { |
| 408 | check_plain (data[i].want, data[i].fmt, mpz_get_si (z)); |
| 409 | } |
| 410 | |
| 411 | check_one (data[i].want, data[i].fmt, z); |
| 412 | |
| 413 | /* Same again, with %N and possibly some high zero limbs */ |
| 414 | nfmt = __gmp_allocate_strdup (data[i].fmt); |
| 415 | for (j = 0; nfmt[j] != '\0'; j++) |
| 416 | if (nfmt[j] == 'Z') |
| 417 | nfmt[j] = 'N'; |
| 418 | for (zeros = 0; zeros <= 3; zeros++) |
| 419 | { |
| 420 | nsize = ABSIZ(z)+zeros; |
| 421 | MPZ_REALLOC (z, nsize); |
| 422 | nsize = (SIZ(z) >= 0 ? nsize : -nsize); |
| 423 | refmpn_zero (PTR(z)+ABSIZ(z), zeros); |
| 424 | check_one (data[i].want, nfmt, PTR(z), nsize); |
| 425 | } |
| 426 | __gmp_free_func (nfmt, strlen(nfmt)+1); |
| 427 | } |
| 428 | |
| 429 | mpz_clear (z); |
| 430 | } |
| 431 | |
| 432 | void |
| 433 | check_q (void) |
| 434 | { |
| 435 | static const struct { |
| 436 | const char *fmt; |
| 437 | const char *q; |
| 438 | const char *want; |
| 439 | } data[] = { |
| 440 | { "%Qd", "0", "0" }, |
| 441 | { "%Qd", "1", "1" }, |
| 442 | { "%Qd", "123", "123" }, |
| 443 | { "%Qd", "-1", "-1" }, |
| 444 | { "%Qd", "-123", "-123" }, |
| 445 | { "%Qd", "3/2", "3/2" }, |
| 446 | { "%Qd", "-3/2", "-3/2" }, |
| 447 | |
| 448 | { "%+Qd", "0", "+0" }, |
| 449 | { "%+Qd", "123", "+123" }, |
| 450 | { "%+Qd", "-123", "-123" }, |
| 451 | { "%+Qd", "5/8", "+5/8" }, |
| 452 | { "%+Qd", "-5/8", "-5/8" }, |
| 453 | |
| 454 | { "%Qx", "123", "7b" }, |
| 455 | { "%QX", "123", "7B" }, |
| 456 | { "%Qx", "15/16", "f/10" }, |
| 457 | { "%QX", "15/16", "F/10" }, |
| 458 | { "%Qx", "-123", "-7b" }, |
| 459 | { "%QX", "-123", "-7B" }, |
| 460 | { "%Qx", "-15/16", "-f/10" }, |
| 461 | { "%QX", "-15/16", "-F/10" }, |
| 462 | { "%Qo", "123", "173" }, |
| 463 | { "%Qo", "-123", "-173" }, |
| 464 | { "%Qo", "16/17", "20/21" }, |
| 465 | { "%Qo", "-16/17", "-20/21" }, |
| 466 | |
| 467 | { "%#Qx", "0", "0" }, |
| 468 | { "%#QX", "0", "0" }, |
| 469 | { "%#Qx", "123", "0x7b" }, |
| 470 | { "%#QX", "123", "0X7B" }, |
| 471 | { "%#Qx", "5/8", "0x5/0x8" }, |
| 472 | { "%#QX", "5/8", "0X5/0X8" }, |
| 473 | { "%#Qx", "-123", "-0x7b" }, |
| 474 | { "%#QX", "-123", "-0X7B" }, |
| 475 | { "%#Qx", "-5/8", "-0x5/0x8" }, |
| 476 | { "%#QX", "-5/8", "-0X5/0X8" }, |
| 477 | { "%#Qo", "0", "0" }, |
| 478 | { "%#Qo", "123", "0173" }, |
| 479 | { "%#Qo", "-123", "-0173" }, |
| 480 | { "%#Qo", "5/7", "05/07" }, |
| 481 | { "%#Qo", "-5/7", "-05/07" }, |
| 482 | |
| 483 | /* zero denominator and showbase */ |
| 484 | { "%#10Qo", "0/0", " 0/0" }, |
| 485 | { "%#10Qd", "0/0", " 0/0" }, |
| 486 | { "%#10Qx", "0/0", " 0/0" }, |
| 487 | { "%#10Qo", "123/0", " 0173/0" }, |
| 488 | { "%#10Qd", "123/0", " 123/0" }, |
| 489 | { "%#10Qx", "123/0", " 0x7b/0" }, |
| 490 | { "%#10QX", "123/0", " 0X7B/0" }, |
| 491 | { "%#10Qo", "-123/0", " -0173/0" }, |
| 492 | { "%#10Qd", "-123/0", " -123/0" }, |
| 493 | { "%#10Qx", "-123/0", " -0x7b/0" }, |
| 494 | { "%#10QX", "-123/0", " -0X7B/0" }, |
| 495 | |
| 496 | { "%10Qd", "0", " 0" }, |
| 497 | { "%-10Qd", "0", "0 " }, |
| 498 | { "%10Qd", "123", " 123" }, |
| 499 | { "%-10Qd", "123", "123 " }, |
| 500 | { "%10Qd", "-123", " -123" }, |
| 501 | { "%-10Qd", "-123", "-123 " }, |
| 502 | |
| 503 | { "%+10Qd", "123", " +123" }, |
| 504 | { "%+-10Qd", "123", "+123 " }, |
| 505 | { "%+10Qd", "-123", " -123" }, |
| 506 | { "%+-10Qd", "-123", "-123 " }, |
| 507 | |
| 508 | { "%08Qd", "0", "00000000" }, |
| 509 | { "%08Qd", "123", "00000123" }, |
| 510 | { "%08Qd", "-123", "-0000123" }, |
| 511 | |
| 512 | { "%+08Qd", "0", "+0000000" }, |
| 513 | { "%+08Qd", "123", "+0000123" }, |
| 514 | { "%+08Qd", "-123", "-0000123" }, |
| 515 | |
| 516 | { "%#08Qx", "0", "00000000" }, |
| 517 | { "%#08Qx", "123", "0x00007b" }, |
| 518 | { "%#08Qx", "-123", "-0x0007b" }, |
| 519 | |
| 520 | { "%+#08Qx", "0", "+0000000" }, |
| 521 | { "%+#08Qx", "123", "+0x0007b" }, |
| 522 | { "%+#08Qx", "-123", "-0x0007b" }, |
| 523 | }; |
| 524 | |
| 525 | int i; |
| 526 | mpq_t q; |
| 527 | |
| 528 | mpq_init (q); |
| 529 | |
| 530 | for (i = 0; i < numberof (data); i++) |
| 531 | { |
| 532 | mpq_set_str_or_abort (q, data[i].q, 0); |
| 533 | check_one (data[i].want, data[i].fmt, q); |
| 534 | } |
| 535 | |
| 536 | mpq_clear (q); |
| 537 | } |
| 538 | |
| 539 | void |
| 540 | check_f (void) |
| 541 | { |
| 542 | static const struct { |
| 543 | const char *fmt; |
| 544 | const char *f; |
| 545 | const char *want; |
| 546 | |
| 547 | } data[] = { |
| 548 | |
| 549 | { "%Ff", "0", "0.000000" }, |
| 550 | { "%Ff", "123", "123.000000" }, |
| 551 | { "%Ff", "-123", "-123.000000" }, |
| 552 | |
| 553 | { "%+Ff", "0", "+0.000000" }, |
| 554 | { "%+Ff", "123", "+123.000000" }, |
| 555 | { "%+Ff", "-123", "-123.000000" }, |
| 556 | |
| 557 | { "%.0Ff", "0", "0" }, |
| 558 | { "%.0Ff", "123", "123" }, |
| 559 | { "%.0Ff", "-123", "-123" }, |
| 560 | |
| 561 | { "%8.0Ff", "0", " 0" }, |
| 562 | { "%8.0Ff", "123", " 123" }, |
| 563 | { "%8.0Ff", "-123", " -123" }, |
| 564 | |
| 565 | { "%08.0Ff", "0", "00000000" }, |
| 566 | { "%08.0Ff", "123", "00000123" }, |
| 567 | { "%08.0Ff", "-123", "-0000123" }, |
| 568 | |
| 569 | { "%10.2Ff", "0", " 0.00" }, |
| 570 | { "%10.2Ff", "0.25", " 0.25" }, |
| 571 | { "%10.2Ff", "123.25", " 123.25" }, |
| 572 | { "%10.2Ff", "-123.25", " -123.25" }, |
| 573 | |
| 574 | { "%-10.2Ff", "0", "0.00 " }, |
| 575 | { "%-10.2Ff", "0.25", "0.25 " }, |
| 576 | { "%-10.2Ff", "123.25", "123.25 " }, |
| 577 | { "%-10.2Ff", "-123.25", "-123.25 " }, |
| 578 | |
| 579 | { "%.2Ff", "0.00000000000001", "0.00" }, |
| 580 | { "%.2Ff", "0.002", "0.00" }, |
| 581 | { "%.2Ff", "0.008", "0.01" }, |
| 582 | |
| 583 | { "%.0Ff", "123.00000000000001", "123" }, |
| 584 | { "%.0Ff", "123.2", "123" }, |
| 585 | { "%.0Ff", "123.8", "124" }, |
| 586 | |
| 587 | { "%.0Ff", "999999.9", "1000000" }, |
| 588 | { "%.0Ff", "3999999.9", "4000000" }, |
| 589 | |
| 590 | { "%Fe", "0", "0.000000e+00" }, |
| 591 | { "%Fe", "1", "1.000000e+00" }, |
| 592 | { "%Fe", "123", "1.230000e+02" }, |
| 593 | |
| 594 | { "%FE", "0", "0.000000E+00" }, |
| 595 | { "%FE", "1", "1.000000E+00" }, |
| 596 | { "%FE", "123", "1.230000E+02" }, |
| 597 | |
| 598 | { "%Fe", "0", "0.000000e+00" }, |
| 599 | { "%Fe", "1", "1.000000e+00" }, |
| 600 | |
| 601 | { "%.0Fe", "10000000000", "1e+10" }, |
| 602 | { "%.0Fe", "-10000000000", "-1e+10" }, |
| 603 | |
| 604 | { "%.2Fe", "10000000000", "1.00e+10" }, |
| 605 | { "%.2Fe", "-10000000000", "-1.00e+10" }, |
| 606 | |
| 607 | { "%8.0Fe", "10000000000", " 1e+10" }, |
| 608 | { "%8.0Fe", "-10000000000", " -1e+10" }, |
| 609 | |
| 610 | { "%-8.0Fe", "10000000000", "1e+10 " }, |
| 611 | { "%-8.0Fe", "-10000000000", "-1e+10 " }, |
| 612 | |
| 613 | { "%12.2Fe", "10000000000", " 1.00e+10" }, |
| 614 | { "%12.2Fe", "-10000000000", " -1.00e+10" }, |
| 615 | |
| 616 | { "%012.2Fe", "10000000000", "00001.00e+10" }, |
| 617 | { "%012.2Fe", "-10000000000", "-0001.00e+10" }, |
| 618 | |
| 619 | { "%Fg", "0", "0" }, |
| 620 | { "%Fg", "1", "1" }, |
| 621 | { "%Fg", "-1", "-1" }, |
| 622 | |
| 623 | { "%.0Fg", "0", "0" }, |
| 624 | { "%.0Fg", "1", "1" }, |
| 625 | { "%.0Fg", "-1", "-1" }, |
| 626 | |
| 627 | { "%.1Fg", "100", "1e+02" }, |
| 628 | { "%.2Fg", "100", "1e+02" }, |
| 629 | { "%.3Fg", "100", "100" }, |
| 630 | { "%.4Fg", "100", "100" }, |
| 631 | |
| 632 | { "%Fg", "0.001", "0.001" }, |
| 633 | { "%Fg", "0.0001", "0.0001" }, |
| 634 | { "%Fg", "0.00001", "1e-05" }, |
| 635 | { "%Fg", "0.000001", "1e-06" }, |
| 636 | |
| 637 | { "%.4Fg", "1.00000000000001", "1" }, |
| 638 | { "%.4Fg", "100000000000001", "1e+14" }, |
| 639 | |
| 640 | { "%.4Fg", "12345678", "1.235e+07" }, |
| 641 | |
| 642 | { "%Fa", "0","0x0p+0" }, |
| 643 | { "%FA", "0","0X0P+0" }, |
| 644 | |
| 645 | { "%Fa", "1","0x1p+0" }, |
| 646 | { "%Fa", "65535","0xf.fffp+12" }, |
| 647 | { "%Fa", "65536","0x1p+16" }, |
| 648 | { "%F.10a", "65536","0x1.0000000000p+16" }, |
| 649 | { "%F.1a", "65535","0x1.0p+16" }, |
| 650 | { "%F.0a", "65535","0x1p+16" }, |
| 651 | |
| 652 | { "%.2Ff", "0.99609375", "1.00" }, |
| 653 | { "%.Ff", "0.99609375", "0.99609375" }, |
| 654 | { "%.Fe", "0.99609375", "9.9609375e-01" }, |
| 655 | { "%.Fg", "0.99609375", "0.99609375" }, |
| 656 | { "%.20Fg", "1000000", "1000000" }, |
| 657 | { "%.Fg", "1000000", "1000000" }, |
| 658 | |
| 659 | { "%#.0Ff", "1", "1." }, |
| 660 | { "%#.0Fe", "1", "1.e+00" }, |
| 661 | { "%#.0Fg", "1", "1." }, |
| 662 | |
| 663 | { "%#.1Ff", "1", "1.0" }, |
| 664 | { "%#.1Fe", "1", "1.0e+00" }, |
| 665 | { "%#.1Fg", "1", "1." }, |
| 666 | |
| 667 | { "%#.4Ff", "1234", "1234.0000" }, |
| 668 | { "%#.4Fe", "1234", "1.2340e+03" }, |
| 669 | { "%#.4Fg", "1234", "1234." }, |
| 670 | |
| 671 | { "%#.8Ff", "1234", "1234.00000000" }, |
| 672 | { "%#.8Fe", "1234", "1.23400000e+03" }, |
| 673 | { "%#.8Fg", "1234", "1234.0000" }, |
| 674 | |
| 675 | }; |
| 676 | |
| 677 | int i; |
| 678 | mpf_t f; |
| 679 | double d; |
| 680 | |
| 681 | mpf_init2 (f, 256L); |
| 682 | |
| 683 | for (i = 0; i < numberof (data); i++) |
| 684 | { |
| 685 | if (data[i].f[0] == '0' && data[i].f[1] == 'x') |
| 686 | mpf_set_str_or_abort (f, data[i].f, 16); |
| 687 | else |
| 688 | mpf_set_str_or_abort (f, data[i].f, 10); |
| 689 | |
| 690 | /* if mpf->double doesn't truncate, then expect same result */ |
| 691 | d = mpf_get_d (f); |
| 692 | if (mpf_cmp_d (f, d) == 0) |
| 693 | check_plain (data[i].want, data[i].fmt, d); |
| 694 | |
| 695 | check_one (data[i].want, data[i].fmt, f); |
| 696 | } |
| 697 | |
| 698 | mpf_clear (f); |
| 699 | } |
| 700 | |
| 701 | |
| 702 | void |
| 703 | check_limb (void) |
| 704 | { |
| 705 | int i; |
| 706 | mp_limb_t limb; |
| 707 | mpz_t z; |
| 708 | char *s; |
| 709 | |
| 710 | check_one ("0", "%Md", CNST_LIMB(0)); |
| 711 | check_one ("1", "%Md", CNST_LIMB(1)); |
| 712 | |
| 713 | /* "i" many 1 bits, tested against mpz_get_str in decimal and hex */ |
| 714 | limb = 1; |
| 715 | mpz_init_set_ui (z, 1L); |
| 716 | for (i = 1; i <= GMP_LIMB_BITS; i++) |
| 717 | { |
| 718 | s = mpz_get_str (NULL, 10, z); |
| 719 | check_one (s, "%Mu", limb); |
| 720 | (*__gmp_free_func) (s, strlen (s) + 1); |
| 721 | |
| 722 | s = mpz_get_str (NULL, 16, z); |
| 723 | check_one (s, "%Mx", limb); |
| 724 | (*__gmp_free_func) (s, strlen (s) + 1); |
| 725 | |
| 726 | s = mpz_get_str (NULL, -16, z); |
| 727 | check_one (s, "%MX", limb); |
| 728 | (*__gmp_free_func) (s, strlen (s) + 1); |
| 729 | |
| 730 | limb = 2*limb + 1; |
| 731 | mpz_mul_2exp (z, z, 1L); |
| 732 | mpz_add_ui (z, z, 1L); |
| 733 | } |
| 734 | |
| 735 | mpz_clear (z); |
| 736 | } |
| 737 | |
| 738 | |
| 739 | void |
| 740 | check_n (void) |
| 741 | { |
| 742 | { |
| 743 | int n = -1; |
| 744 | check_one ("blah", "%nblah", &n); |
| 745 | ASSERT_ALWAYS (n == 0); |
| 746 | } |
| 747 | |
| 748 | { |
| 749 | int n = -1; |
| 750 | check_one ("hello ", "hello %n", &n); |
| 751 | ASSERT_ALWAYS (n == 6); |
| 752 | } |
| 753 | |
| 754 | { |
| 755 | int n = -1; |
| 756 | check_one ("hello world", "hello %n world", &n); |
| 757 | ASSERT_ALWAYS (n == 6); |
| 758 | } |
| 759 | |
| 760 | #define CHECK_N(type, string) \ |
| 761 | do { \ |
| 762 | type x[2]; \ |
| 763 | char fmt[128]; \ |
| 764 | \ |
| 765 | x[0] = ~ (type) 0; \ |
| 766 | x[1] = ~ (type) 0; \ |
| 767 | sprintf (fmt, "%%d%%%sn%%d", string); \ |
| 768 | check_one ("123456", fmt, 123, &x[0], 456); \ |
| 769 | \ |
| 770 | /* should write whole of x[0] and none of x[1] */ \ |
| 771 | ASSERT_ALWAYS (x[0] == 3); \ |
| 772 | ASSERT_ALWAYS (x[1] == (type) ~ (type) 0); \ |
| 773 | \ |
| 774 | } while (0) |
| 775 | |
| 776 | CHECK_N (mp_limb_t, "M"); |
| 777 | CHECK_N (char, "hh"); |
| 778 | CHECK_N (long, "l"); |
| 779 | #if HAVE_LONG_LONG |
| 780 | CHECK_N (long long, "L"); |
| 781 | #endif |
| 782 | #if HAVE_INTMAX_T |
| 783 | CHECK_N (intmax_t, "j"); |
| 784 | #endif |
| 785 | #if HAVE_PTRDIFF_T |
| 786 | CHECK_N (ptrdiff_t, "t"); |
| 787 | #endif |
| 788 | CHECK_N (short, "h"); |
| 789 | CHECK_N (size_t, "z"); |
| 790 | |
| 791 | { |
| 792 | mpz_t x[2]; |
| 793 | mpz_init_set_si (x[0], -987L); |
| 794 | mpz_init_set_si (x[1], 654L); |
| 795 | check_one ("123456", "%d%Zn%d", 123, x[0], 456); |
| 796 | MPZ_CHECK_FORMAT (x[0]); |
| 797 | MPZ_CHECK_FORMAT (x[1]); |
| 798 | ASSERT_ALWAYS (mpz_cmp_ui (x[0], 3L) == 0); |
| 799 | ASSERT_ALWAYS (mpz_cmp_ui (x[1], 654L) == 0); |
| 800 | mpz_clear (x[0]); |
| 801 | mpz_clear (x[1]); |
| 802 | } |
| 803 | |
| 804 | { |
| 805 | mpq_t x[2]; |
| 806 | mpq_init (x[0]); |
| 807 | mpq_init (x[1]); |
| 808 | mpq_set_ui (x[0], 987L, 654L); |
| 809 | mpq_set_ui (x[1], 4115L, 226L); |
| 810 | check_one ("123456", "%d%Qn%d", 123, x[0], 456); |
| 811 | MPQ_CHECK_FORMAT (x[0]); |
| 812 | MPQ_CHECK_FORMAT (x[1]); |
| 813 | ASSERT_ALWAYS (mpq_cmp_ui (x[0], 3L, 1L) == 0); |
| 814 | ASSERT_ALWAYS (mpq_cmp_ui (x[1], 4115L, 226L) == 0); |
| 815 | mpq_clear (x[0]); |
| 816 | mpq_clear (x[1]); |
| 817 | } |
| 818 | |
| 819 | { |
| 820 | mpf_t x[2]; |
| 821 | mpf_init (x[0]); |
| 822 | mpf_init (x[1]); |
| 823 | mpf_set_ui (x[0], 987L); |
| 824 | mpf_set_ui (x[1], 654L); |
| 825 | check_one ("123456", "%d%Fn%d", 123, x[0], 456); |
| 826 | MPF_CHECK_FORMAT (x[0]); |
| 827 | MPF_CHECK_FORMAT (x[1]); |
| 828 | ASSERT_ALWAYS (mpf_cmp_ui (x[0], 3L) == 0); |
| 829 | ASSERT_ALWAYS (mpf_cmp_ui (x[1], 654L) == 0); |
| 830 | mpf_clear (x[0]); |
| 831 | mpf_clear (x[1]); |
| 832 | } |
| 833 | |
| 834 | { |
| 835 | mp_limb_t a[5]; |
| 836 | mp_limb_t a_want[numberof(a)]; |
| 837 | mp_size_t i; |
| 838 | |
| 839 | a[0] = 123; |
| 840 | check_one ("blah", "bl%Nnah", a, (mp_size_t) 0); |
| 841 | ASSERT_ALWAYS (a[0] == 123); |
| 842 | |
| 843 | MPN_ZERO (a_want, numberof (a_want)); |
| 844 | for (i = 1; i < numberof (a); i++) |
| 845 | { |
| 846 | check_one ("blah", "bl%Nnah", a, i); |
| 847 | a_want[0] = 2; |
| 848 | ASSERT_ALWAYS (mpn_cmp (a, a_want, i) == 0); |
| 849 | } |
| 850 | } |
| 851 | } |
| 852 | |
| 853 | |
| 854 | void |
| 855 | check_misc (void) |
| 856 | { |
| 857 | mpz_t z; |
| 858 | mpf_t f; |
| 859 | |
| 860 | mpz_init (z); |
| 861 | mpf_init2 (f, 128L); |
| 862 | |
| 863 | check_one ("!", "%c", '!'); |
| 864 | |
| 865 | check_one ("hello world", "hello %s", "world"); |
| 866 | check_one ("hello:", "%s:", "hello"); |
| 867 | mpz_set_ui (z, 0L); |
| 868 | check_one ("hello0", "%s%Zd", "hello", z, z); |
| 869 | |
| 870 | { |
| 871 | static char xs[801]; |
| 872 | memset (xs, 'x', sizeof(xs)-1); |
| 873 | check_one (xs, "%s", xs); |
| 874 | } |
| 875 | { |
| 876 | char *xs; |
| 877 | xs = (char *) (*__gmp_allocate_func) (MAX_OUTPUT * 2 - 12); |
| 878 | memset (xs, '%', MAX_OUTPUT * 2 - 14); |
| 879 | xs [MAX_OUTPUT * 2 - 13] = '\0'; |
| 880 | xs [MAX_OUTPUT * 2 - 14] = 'x'; |
| 881 | check_one (xs + MAX_OUTPUT - 7, xs, NULL); |
| 882 | (*__gmp_free_func) (xs, MAX_OUTPUT * 2 - 12); |
| 883 | } |
| 884 | |
| 885 | mpz_set_ui (z, 12345L); |
| 886 | check_one (" 12345", "%*Zd", 10, z); |
| 887 | check_one ("0000012345", "%0*Zd", 10, z); |
| 888 | check_one ("12345 ", "%*Zd", -10, z); |
| 889 | check_one ("12345 and 678", "%Zd and %d", z, 678); |
| 890 | check_one ("12345,1,12345,2,12345", "%Zd,%d,%Zd,%d,%Zd", z, 1, z, 2, z); |
| 891 | |
| 892 | /* from the glibc info docs */ |
| 893 | mpz_set_si (z, 0L); |
| 894 | check_one ("| 0|0 | +0|+0 | 0|00000| | 00|0|", |
| 895 | "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| 896 | /**/ z, z, z, z, z, z, z, z, z); |
| 897 | mpz_set_si (z, 1L); |
| 898 | check_one ("| 1|1 | +1|+1 | 1|00001| 1| 01|1|", |
| 899 | "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| 900 | /**/ z, z, z, z, z, z, z, z, z); |
| 901 | mpz_set_si (z, -1L); |
| 902 | check_one ("| -1|-1 | -1|-1 | -1|-0001| -1| -01|-1|", |
| 903 | "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| 904 | /**/ z, z, z, z, z, z, z, z, z); |
| 905 | mpz_set_si (z, 100000L); |
| 906 | check_one ("|100000|100000|+100000|+100000| 100000|100000|100000|100000|100000|", |
| 907 | "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| 908 | /**/ z, z, z, z, z, z, z, z, z); |
| 909 | mpz_set_si (z, 0L); |
| 910 | check_one ("| 0| 0| 0| 0| 0| 0| 00000000|", |
| 911 | "|%5Zo|%5Zx|%5ZX|%#5Zo|%#5Zx|%#5ZX|%#10.8Zx|", |
| 912 | /**/ z, z, z, z, z, z, z); |
| 913 | mpz_set_si (z, 1L); |
| 914 | check_one ("| 1| 1| 1| 01| 0x1| 0X1|0x00000001|", |
| 915 | "|%5Zo|%5Zx|%5ZX|%#5Zo|%#5Zx|%#5ZX|%#10.8Zx|", |
| 916 | /**/ z, z, z, z, z, z, z); |
| 917 | mpz_set_si (z, 100000L); |
| 918 | check_one ("|303240|186a0|186A0|0303240|0x186a0|0X186A0|0x000186a0|", |
| 919 | "|%5Zo|%5Zx|%5ZX|%#5Zo|%#5Zx|%#5ZX|%#10.8Zx|", |
| 920 | /**/ z, z, z, z, z, z, z); |
| 921 | |
| 922 | /* %zd for size_t won't be available on old systems, and running something |
| 923 | to see if it works might be bad, so only try it on glibc, and only on a |
| 924 | new enough version (glibc 2.0 doesn't have %zd) */ |
| 925 | #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 0) |
| 926 | mpz_set_ui (z, 789L); |
| 927 | check_one ("456 789 blah", "%zd %Zd blah", (size_t) 456, z); |
| 928 | #endif |
| 929 | |
| 930 | mpz_clear (z); |
| 931 | mpf_clear (f); |
| 932 | } |
| 933 | |
| 934 | |
| 935 | int |
| 936 | main (int argc, char *argv[]) |
| 937 | { |
| 938 | if (argc > 1 && strcmp (argv[1], "-s") == 0) |
| 939 | option_check_printf = 1; |
| 940 | |
| 941 | tests_start (); |
| 942 | check_vfprintf_fp = fopen (CHECK_VFPRINTF_FILENAME, "w+"); |
| 943 | ASSERT_ALWAYS (check_vfprintf_fp != NULL); |
| 944 | |
| 945 | check_z (); |
| 946 | check_q (); |
| 947 | check_f (); |
| 948 | check_limb (); |
| 949 | check_n (); |
| 950 | check_misc (); |
| 951 | |
| 952 | ASSERT_ALWAYS (fclose (check_vfprintf_fp) == 0); |
| 953 | unlink (CHECK_VFPRINTF_FILENAME); |
| 954 | tests_end (); |
| 955 | exit (0); |
| 956 | } |