1/* Formatted output to strings. 2 Copyright (C) 1999-2000, 2002-2003, 2006-2007 Free Software Foundation, Inc. 3 4 This program is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Library General Public License as published 6 by the Free Software Foundation; either version 2, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public 15 License along with this program; if not, write to the Free Software 16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 17 USA. */ 18 19/* This file can be parametrized with the following macros: 20 CHAR_T The element type of the format string. 21 CHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters 22 in the format string are ASCII. 23 DIRECTIVE Structure denoting a format directive. 24 Depends on CHAR_T. 25 DIRECTIVES Structure denoting the set of format directives of a 26 format string. Depends on CHAR_T. 27 PRINTF_PARSE Function that parses a format string. 28 Depends on CHAR_T. 29 STATIC Set to 'static' to declare the function static. 30 ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. */ 31 32#ifndef PRINTF_PARSE 33# include <config.h> 34#endif 35 36/* Specification. */ 37#ifndef PRINTF_PARSE 38# include "printf-parse.h" 39#endif 40 41/* Default parameters. */ 42#ifndef PRINTF_PARSE 43# define PRINTF_PARSE printf_parse 44# define CHAR_T char 45# define DIRECTIVE char_directive 46# define DIRECTIVES char_directives 47#endif 48 49/* Get size_t, NULL. */ 50#include <stddef.h> 51 52/* Get intmax_t. */ 53#if defined IN_LIBINTL || defined IN_LIBASPRINTF 54# if HAVE_STDINT_H_WITH_UINTMAX 55# include <stdint.h> 56# endif 57# if HAVE_INTTYPES_H_WITH_UINTMAX 58# include <inttypes.h> 59# endif 60#else 61# include <stdint.h> 62#endif 63 64/* malloc(), realloc(), free(). */ 65#include <stdlib.h> 66 67/* errno. */ 68#include <errno.h> 69 70/* Checked size_t computations. */ 71#include "xsize.h" 72 73#if CHAR_T_ONLY_ASCII 74/* c_isascii(). */ 75# include "c-ctype.h" 76#endif 77 78#ifdef STATIC 79STATIC 80#endif 81int 82PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a) 83{ 84 const CHAR_T *cp = format; /* pointer into format */ 85 size_t arg_posn = 0; /* number of regular arguments consumed */ 86 size_t d_allocated; /* allocated elements of d->dir */ 87 size_t a_allocated; /* allocated elements of a->arg */ 88 size_t max_width_length = 0; 89 size_t max_precision_length = 0; 90 91 d->count = 0; 92 d_allocated = 1; 93 d->dir = (DIRECTIVE *) malloc (d_allocated * sizeof (DIRECTIVE)); 94 if (d->dir == NULL) 95 /* Out of memory. */ 96 goto out_of_memory_1; 97 98 a->count = 0; 99 a_allocated = 0; 100 a->arg = NULL; 101 102#define REGISTER_ARG(_index_,_type_) \ 103 { \ 104 size_t n = (_index_); \ 105 if (n >= a_allocated) \ 106 { \ 107 size_t memory_size; \ 108 argument *memory; \ 109 \ 110 a_allocated = xtimes (a_allocated, 2); \ 111 if (a_allocated <= n) \ 112 a_allocated = xsum (n, 1); \ 113 memory_size = xtimes (a_allocated, sizeof (argument)); \ 114 if (size_overflow_p (memory_size)) \ 115 /* Overflow, would lead to out of memory. */ \ 116 goto out_of_memory; \ 117 memory = (argument *) (a->arg \ 118 ? realloc (a->arg, memory_size) \ 119 : malloc (memory_size)); \ 120 if (memory == NULL) \ 121 /* Out of memory. */ \ 122 goto out_of_memory; \ 123 a->arg = memory; \ 124 } \ 125 while (a->count <= n) \ 126 a->arg[a->count++].type = TYPE_NONE; \ 127 if (a->arg[n].type == TYPE_NONE) \ 128 a->arg[n].type = (_type_); \ 129 else if (a->arg[n].type != (_type_)) \ 130 /* Ambiguous type for positional argument. */ \ 131 goto error; \ 132 } 133 134 while (*cp != '\0') 135 { 136 CHAR_T c = *cp++; 137 if (c == '%') 138 { 139 size_t arg_index = ARG_NONE; 140 DIRECTIVE *dp = &d->dir[d->count]; /* pointer to next directive */ 141 142 /* Initialize the next directive. */ 143 dp->dir_start = cp - 1; 144 dp->flags = 0; 145 dp->width_start = NULL; 146 dp->width_end = NULL; 147 dp->width_arg_index = ARG_NONE; 148 dp->precision_start = NULL; 149 dp->precision_end = NULL; 150 dp->precision_arg_index = ARG_NONE; 151 dp->arg_index = ARG_NONE; 152 153 /* Test for positional argument. */ 154 if (*cp >= '0' && *cp <= '9') 155 { 156 const CHAR_T *np; 157 158 for (np = cp; *np >= '0' && *np <= '9'; np++) 159 ; 160 if (*np == '$') 161 { 162 size_t n = 0; 163 164 for (np = cp; *np >= '0' && *np <= '9'; np++) 165 n = xsum (xtimes (n, 10), *np - '0'); 166 if (n == 0) 167 /* Positional argument 0. */ 168 goto error; 169 if (size_overflow_p (n)) 170 /* n too large, would lead to out of memory later. */ 171 goto error; 172 arg_index = n - 1; 173 cp = np + 1; 174 } 175 } 176 177 /* Read the flags. */ 178 for (;;) 179 { 180 if (*cp == '\'') 181 { 182 dp->flags |= FLAG_GROUP; 183 cp++; 184 } 185 else if (*cp == '-') 186 { 187 dp->flags |= FLAG_LEFT; 188 cp++; 189 } 190 else if (*cp == '+') 191 { 192 dp->flags |= FLAG_SHOWSIGN; 193 cp++; 194 } 195 else if (*cp == ' ') 196 { 197 dp->flags |= FLAG_SPACE; 198 cp++; 199 } 200 else if (*cp == '#') 201 { 202 dp->flags |= FLAG_ALT; 203 cp++; 204 } 205 else if (*cp == '0') 206 { 207 dp->flags |= FLAG_ZERO; 208 cp++; 209 } 210 else 211 break; 212 } 213 214 /* Parse the field width. */ 215 if (*cp == '*') 216 { 217 dp->width_start = cp; 218 cp++; 219 dp->width_end = cp; 220 if (max_width_length < 1) 221 max_width_length = 1; 222 223 /* Test for positional argument. */ 224 if (*cp >= '0' && *cp <= '9') 225 { 226 const CHAR_T *np; 227 228 for (np = cp; *np >= '0' && *np <= '9'; np++) 229 ; 230 if (*np == '$') 231 { 232 size_t n = 0; 233 234 for (np = cp; *np >= '0' && *np <= '9'; np++) 235 n = xsum (xtimes (n, 10), *np - '0'); 236 if (n == 0) 237 /* Positional argument 0. */ 238 goto error; 239 if (size_overflow_p (n)) 240 /* n too large, would lead to out of memory later. */ 241 goto error; 242 dp->width_arg_index = n - 1; 243 cp = np + 1; 244 } 245 } 246 if (dp->width_arg_index == ARG_NONE) 247 { 248 dp->width_arg_index = arg_posn++; 249 if (dp->width_arg_index == ARG_NONE) 250 /* arg_posn wrapped around. */ 251 goto error; 252 } 253 REGISTER_ARG (dp->width_arg_index, TYPE_INT); 254 } 255 else if (*cp >= '0' && *cp <= '9') 256 { 257 size_t width_length; 258 259 dp->width_start = cp; 260 for (; *cp >= '0' && *cp <= '9'; cp++) 261 ; 262 dp->width_end = cp; 263 width_length = dp->width_end - dp->width_start; 264 if (max_width_length < width_length) 265 max_width_length = width_length; 266 } 267 268 /* Parse the precision. */ 269 if (*cp == '.') 270 { 271 cp++; 272 if (*cp == '*') 273 { 274 dp->precision_start = cp - 1; 275 cp++; 276 dp->precision_end = cp; 277 if (max_precision_length < 2) 278 max_precision_length = 2; 279 280 /* Test for positional argument. */ 281 if (*cp >= '0' && *cp <= '9') 282 { 283 const CHAR_T *np; 284 285 for (np = cp; *np >= '0' && *np <= '9'; np++) 286 ; 287 if (*np == '$') 288 { 289 size_t n = 0; 290 291 for (np = cp; *np >= '0' && *np <= '9'; np++) 292 n = xsum (xtimes (n, 10), *np - '0'); 293 if (n == 0) 294 /* Positional argument 0. */ 295 goto error; 296 if (size_overflow_p (n)) 297 /* n too large, would lead to out of memory 298 later. */ 299 goto error; 300 dp->precision_arg_index = n - 1; 301 cp = np + 1; 302 } 303 } 304 if (dp->precision_arg_index == ARG_NONE) 305 { 306 dp->precision_arg_index = arg_posn++; 307 if (dp->precision_arg_index == ARG_NONE) 308 /* arg_posn wrapped around. */ 309 goto error; 310 } 311 REGISTER_ARG (dp->precision_arg_index, TYPE_INT); 312 } 313 else 314 { 315 size_t precision_length; 316 317 dp->precision_start = cp - 1; 318 for (; *cp >= '0' && *cp <= '9'; cp++) 319 ; 320 dp->precision_end = cp; 321 precision_length = dp->precision_end - dp->precision_start; 322 if (max_precision_length < precision_length) 323 max_precision_length = precision_length; 324 } 325 } 326 327 { 328 arg_type type; 329 330 /* Parse argument type/size specifiers. */ 331 { 332 int flags = 0; 333 334 for (;;) 335 { 336 if (*cp == 'h') 337 { 338 flags |= (1 << (flags & 1)); 339 cp++; 340 } 341 else if (*cp == 'L') 342 { 343 flags |= 4; 344 cp++; 345 } 346 else if (*cp == 'l') 347 { 348 flags += 8; 349 cp++; 350 } 351 else if (*cp == 'j') 352 { 353 if (sizeof (intmax_t) > sizeof (long)) 354 { 355 /* intmax_t = long long */ 356 flags += 16; 357 } 358 else if (sizeof (intmax_t) > sizeof (int)) 359 { 360 /* intmax_t = long */ 361 flags += 8; 362 } 363 cp++; 364 } 365 else if (*cp == 'z' || *cp == 'Z') 366 { 367 /* 'z' is standardized in ISO C 99, but glibc uses 'Z' 368 because the warning facility in gcc-2.95.2 understands 369 only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ 370 if (sizeof (size_t) > sizeof (long)) 371 { 372 /* size_t = long long */ 373 flags += 16; 374 } 375 else if (sizeof (size_t) > sizeof (int)) 376 { 377 /* size_t = long */ 378 flags += 8; 379 } 380 cp++; 381 } 382 else if (*cp == 't') 383 { 384 if (sizeof (ptrdiff_t) > sizeof (long)) 385 { 386 /* ptrdiff_t = long long */ 387 flags += 16; 388 } 389 else if (sizeof (ptrdiff_t) > sizeof (int)) 390 { 391 /* ptrdiff_t = long */ 392 flags += 8; 393 } 394 cp++; 395 } 396 else 397 break; 398 } 399 400 /* Read the conversion character. */ 401 c = *cp++; 402 switch (c) 403 { 404 case 'd': case 'i': 405#if HAVE_LONG_LONG_INT 406 /* If 'long long' exists and is larger than 'long': */ 407 if (flags >= 16 || (flags & 4)) 408 type = TYPE_LONGLONGINT; 409 else 410#endif 411 /* If 'long long' exists and is the same as 'long', we parse 412 "lld" into TYPE_LONGINT. */ 413 if (flags >= 8) 414 type = TYPE_LONGINT; 415 else if (flags & 2) 416 type = TYPE_SCHAR; 417 else if (flags & 1) 418 type = TYPE_SHORT; 419 else 420 type = TYPE_INT; 421 break; 422 case 'o': case 'u': case 'x': case 'X': 423#if HAVE_LONG_LONG_INT 424 /* If 'long long' exists and is larger than 'long': */ 425 if (flags >= 16 || (flags & 4)) 426 type = TYPE_ULONGLONGINT; 427 else 428#endif 429 /* If 'unsigned long long' exists and is the same as 430 'unsigned long', we parse "llu" into TYPE_ULONGINT. */ 431 if (flags >= 8) 432 type = TYPE_ULONGINT; 433 else if (flags & 2) 434 type = TYPE_UCHAR; 435 else if (flags & 1) 436 type = TYPE_USHORT; 437 else 438 type = TYPE_UINT; 439 break; 440 case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': 441 case 'a': case 'A': 442 if (flags >= 16 || (flags & 4)) 443 type = TYPE_LONGDOUBLE; 444 else 445 type = TYPE_DOUBLE; 446 break; 447 case 'c': 448 if (flags >= 8) 449#if HAVE_WINT_T 450 type = TYPE_WIDE_CHAR; 451#else 452 goto error; 453#endif 454 else 455 type = TYPE_CHAR; 456 break; 457#if HAVE_WINT_T 458 case 'C': 459 type = TYPE_WIDE_CHAR; 460 c = 'c'; 461 break; 462#endif 463 case 's': 464 if (flags >= 8) 465#if HAVE_WCHAR_T 466 type = TYPE_WIDE_STRING; 467#else 468 goto error; 469#endif 470 else 471 type = TYPE_STRING; 472 break; 473#if HAVE_WCHAR_T 474 case 'S': 475 type = TYPE_WIDE_STRING; 476 c = 's'; 477 break; 478#endif 479 case 'p': 480 type = TYPE_POINTER; 481 break; 482 case 'n': 483#if HAVE_LONG_LONG_INT 484 /* If 'long long' exists and is larger than 'long': */ 485 if (flags >= 16 || (flags & 4)) 486 type = TYPE_COUNT_LONGLONGINT_POINTER; 487 else 488#endif 489 /* If 'long long' exists and is the same as 'long', we parse 490 "lln" into TYPE_COUNT_LONGINT_POINTER. */ 491 if (flags >= 8) 492 type = TYPE_COUNT_LONGINT_POINTER; 493 else if (flags & 2) 494 type = TYPE_COUNT_SCHAR_POINTER; 495 else if (flags & 1) 496 type = TYPE_COUNT_SHORT_POINTER; 497 else 498 type = TYPE_COUNT_INT_POINTER; 499 break; 500#if ENABLE_UNISTDIO 501 /* The unistdio extensions. */ 502 case 'U': 503 if (flags >= 16) 504 type = TYPE_U32_STRING; 505 else if (flags >= 8) 506 type = TYPE_U16_STRING; 507 else 508 type = TYPE_U8_STRING; 509 break; 510#endif 511 case '%': 512 type = TYPE_NONE; 513 break; 514 default: 515 /* Unknown conversion character. */ 516 goto error; 517 } 518 } 519 520 if (type != TYPE_NONE) 521 { 522 dp->arg_index = arg_index; 523 if (dp->arg_index == ARG_NONE) 524 { 525 dp->arg_index = arg_posn++; 526 if (dp->arg_index == ARG_NONE) 527 /* arg_posn wrapped around. */ 528 goto error; 529 } 530 REGISTER_ARG (dp->arg_index, type); 531 } 532 dp->conversion = c; 533 dp->dir_end = cp; 534 } 535 536 d->count++; 537 if (d->count >= d_allocated) 538 { 539 size_t memory_size; 540 DIRECTIVE *memory; 541 542 d_allocated = xtimes (d_allocated, 2); 543 memory_size = xtimes (d_allocated, sizeof (DIRECTIVE)); 544 if (size_overflow_p (memory_size)) 545 /* Overflow, would lead to out of memory. */ 546 goto out_of_memory; 547 memory = (DIRECTIVE *) realloc (d->dir, memory_size); 548 if (memory == NULL) 549 /* Out of memory. */ 550 goto out_of_memory; 551 d->dir = memory; 552 } 553 } 554#if CHAR_T_ONLY_ASCII 555 else if (!c_isascii (c)) 556 { 557 /* Non-ASCII character. Not supported. */ 558 goto error; 559 } 560#endif 561 } 562 d->dir[d->count].dir_start = cp; 563 564 d->max_width_length = max_width_length; 565 d->max_precision_length = max_precision_length; 566 return 0; 567 568error: 569 if (a->arg) 570 free (a->arg); 571 if (d->dir) 572 free (d->dir); 573 errno = EINVAL; 574 return -1; 575 576out_of_memory: 577 if (a->arg) 578 free (a->arg); 579 if (d->dir) 580 free (d->dir); 581out_of_memory_1: 582 errno = ENOMEM; 583 return -1; 584} 585 586#undef PRINTF_PARSE 587#undef DIRECTIVES 588#undef DIRECTIVE 589#undef CHAR_T_ONLY_ASCII 590#undef CHAR_T 591