1/* $NetBSD$ */ 2 3/* 4 * Copyright (C) 2004, 2005, 2007, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1999-2001, 2003 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20/* Id */ 21 22#include <config.h> 23 24#include <ctype.h> 25#include <stdio.h> /* for sprintf */ 26#include <string.h> 27 28#define LWRES__PRINT_SOURCE /* Used to get the lwres_print_* prototypes. */ 29 30#include <lwres/stdlib.h> 31 32#include "assert_p.h" 33#include "print_p.h" 34 35#define LWRES_PRINT_QUADFORMAT LWRES_PLATFORM_QUADFORMAT 36 37int 38lwres__print_sprintf(char *str, const char *format, ...) { 39 va_list ap; 40 41 va_start(ap, format); 42 vsprintf(str, format, ap); 43 va_end(ap); 44 return (strlen(str)); 45} 46 47/* 48 * Return length of string that would have been written if not truncated. 49 */ 50 51int 52lwres__print_snprintf(char *str, size_t size, const char *format, ...) { 53 va_list ap; 54 int ret; 55 56 va_start(ap, format); 57 ret = vsnprintf(str, size, format, ap); 58 va_end(ap); 59 return (ret); 60 61} 62 63/* 64 * Return length of string that would have been written if not truncated. 65 */ 66 67int 68lwres__print_vsnprintf(char *str, size_t size, const char *format, va_list ap) { 69 int h; 70 int l; 71 int q; 72 int alt; 73 int zero; 74 int left; 75 int plus; 76 int space; 77 long long tmpi; 78 unsigned long long tmpui; 79 unsigned long width; 80 unsigned long precision; 81 unsigned int length; 82 char buf[1024]; 83 char c; 84 void *v; 85 char *save = str; 86 const char *cp; 87 const char *head; 88 int count = 0; 89 int pad; 90 int zeropad; 91 int dot; 92 double dbl; 93#ifdef HAVE_LONG_DOUBLE 94 long double ldbl; 95#endif 96 char fmt[32]; 97 98 INSIST(str != NULL); 99 INSIST(format != NULL); 100 101 while (*format != '\0') { 102 if (*format != '%') { 103 if (size > 1U) { 104 *str++ = *format; 105 size--; 106 } 107 count++; 108 format++; 109 continue; 110 } 111 format++; 112 113 /* 114 * Reset flags. 115 */ 116 dot = space = plus = left = zero = alt = h = l = q = 0; 117 width = precision = 0; 118 head = ""; 119 length = pad = zeropad = 0; 120 POST(length); 121 122 do { 123 if (*format == '#') { 124 alt = 1; 125 format++; 126 } else if (*format == '-') { 127 left = 1; 128 zero = 0; 129 format++; 130 } else if (*format == ' ') { 131 if (!plus) 132 space = 1; 133 format++; 134 } else if (*format == '+') { 135 plus = 1; 136 space = 0; 137 format++; 138 } else if (*format == '0') { 139 if (!left) 140 zero = 1; 141 format++; 142 } else 143 break; 144 } while (1); 145 146 /* 147 * Width. 148 */ 149 if (*format == '*') { 150 width = va_arg(ap, int); 151 format++; 152 } else if (isdigit((unsigned char)*format)) { 153 char *e; 154 width = strtoul(format, &e, 10); 155 format = e; 156 } 157 158 /* 159 * Precision. 160 */ 161 if (*format == '.') { 162 format++; 163 dot = 1; 164 if (*format == '*') { 165 precision = va_arg(ap, int); 166 format++; 167 } else if (isdigit((unsigned char)*format)) { 168 char *e; 169 precision = strtoul(format, &e, 10); 170 format = e; 171 } 172 } 173 174 switch (*format) { 175 case '\0': 176 continue; 177 case '%': 178 if (size > 1U) { 179 *str++ = *format; 180 size--; 181 } 182 count++; 183 break; 184 case 'q': 185 q = 1; 186 format++; 187 goto doint; 188 case 'h': 189 h = 1; 190 format++; 191 goto doint; 192 case 'l': 193 l = 1; 194 format++; 195 if (*format == 'l') { 196 q = 1; 197 format++; 198 } 199 goto doint; 200 case 'n': 201 case 'i': 202 case 'd': 203 case 'o': 204 case 'u': 205 case 'x': 206 case 'X': 207 doint: 208 if (precision != 0U) 209 zero = 0; 210 switch (*format) { 211 case 'n': 212 if (h) { 213 short int *p; 214 p = va_arg(ap, short *); 215 REQUIRE(p != NULL); 216 *p = str - save; 217 } else if (l) { 218 long int *p; 219 p = va_arg(ap, long *); 220 REQUIRE(p != NULL); 221 *p = str - save; 222 } else { 223 int *p; 224 p = va_arg(ap, int *); 225 REQUIRE(p != NULL); 226 *p = str - save; 227 } 228 break; 229 case 'i': 230 case 'd': 231 if (q) 232 tmpi = va_arg(ap, long long int); 233 else if (l) 234 tmpi = va_arg(ap, long int); 235 else 236 tmpi = va_arg(ap, int); 237 if (tmpi < 0) { 238 head = "-"; 239 tmpui = -tmpi; 240 } else { 241 if (plus) 242 head = "+"; 243 else if (space) 244 head = " "; 245 else 246 head = ""; 247 tmpui = tmpi; 248 } 249 sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u", 250 tmpui); 251 goto printint; 252 case 'o': 253 if (q) 254 tmpui = va_arg(ap, 255 unsigned long long int); 256 else if (l) 257 tmpui = va_arg(ap, long int); 258 else 259 tmpui = va_arg(ap, int); 260 sprintf(buf, 261 alt ? "%#" LWRES_PRINT_QUADFORMAT "o" 262 : "%" LWRES_PRINT_QUADFORMAT "o", 263 tmpui); 264 goto printint; 265 case 'u': 266 if (q) 267 tmpui = va_arg(ap, 268 unsigned long long int); 269 else if (l) 270 tmpui = va_arg(ap, unsigned long int); 271 else 272 tmpui = va_arg(ap, unsigned int); 273 sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u", 274 tmpui); 275 goto printint; 276 case 'x': 277 if (q) 278 tmpui = va_arg(ap, 279 unsigned long long int); 280 else if (l) 281 tmpui = va_arg(ap, unsigned long int); 282 else 283 tmpui = va_arg(ap, unsigned int); 284 if (alt) { 285 head = "0x"; 286 if (precision > 2U) 287 precision -= 2; 288 } 289 sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "x", 290 tmpui); 291 goto printint; 292 case 'X': 293 if (q) 294 tmpui = va_arg(ap, 295 unsigned long long int); 296 else if (l) 297 tmpui = va_arg(ap, unsigned long int); 298 else 299 tmpui = va_arg(ap, unsigned int); 300 if (alt) { 301 head = "0X"; 302 if (precision > 2U) 303 precision -= 2; 304 } 305 sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "X", 306 tmpui); 307 goto printint; 308 printint: 309 if (precision != 0U || width != 0U) { 310 length = strlen(buf); 311 if (length < precision) 312 zeropad = precision - length; 313 else if (length < width && zero) 314 zeropad = width - length; 315 if (width != 0U) { 316 pad = width - length - 317 zeropad - strlen(head); 318 if (pad < 0) 319 pad = 0; 320 } 321 } 322 count += strlen(head) + strlen(buf) + pad + 323 zeropad; 324 if (!left) { 325 while (pad > 0 && size > 1U) { 326 *str++ = ' '; 327 size--; 328 pad--; 329 } 330 } 331 cp = head; 332 while (*cp != '\0' && size > 1U) { 333 *str++ = *cp++; 334 size--; 335 } 336 while (zeropad > 0 && size > 1U) { 337 *str++ = '0'; 338 size--; 339 zeropad--; 340 } 341 cp = buf; 342 while (*cp != '\0' && size > 1U) { 343 *str++ = *cp++; 344 size--; 345 } 346 while (pad > 0 && size > 1U) { 347 *str++ = ' '; 348 size--; 349 pad--; 350 } 351 break; 352 default: 353 break; 354 } 355 break; 356 case 's': 357 cp = va_arg(ap, char *); 358 REQUIRE(cp != NULL); 359 360 if (precision != 0U) { 361 /* 362 * cp need not be NULL terminated. 363 */ 364 const char *tp; 365 unsigned long n; 366 367 n = precision; 368 tp = cp; 369 while (n != 0U && *tp != '\0') 370 n--, tp++; 371 length = precision - n; 372 } else { 373 length = strlen(cp); 374 } 375 if (width != 0U) { 376 pad = width - length; 377 if (pad < 0) 378 pad = 0; 379 } 380 count += pad + length; 381 if (!left) 382 while (pad > 0 && size > 1U) { 383 *str++ = ' '; 384 size--; 385 pad--; 386 } 387 if (precision != 0U) 388 while (precision > 0U && *cp != '\0' && 389 size > 1U) { 390 *str++ = *cp++; 391 size--; 392 precision--; 393 } 394 else 395 while (*cp != '\0' && size > 1U) { 396 *str++ = *cp++; 397 size--; 398 } 399 while (pad > 0 && size > 1U) { 400 *str++ = ' '; 401 size--; 402 pad--; 403 } 404 break; 405 case 'c': 406 c = va_arg(ap, int); 407 if (width > 0U) { 408 count += width; 409 width--; 410 if (left) { 411 *str++ = c; 412 size--; 413 } 414 while (width-- > 0U && size > 1U) { 415 *str++ = ' '; 416 size--; 417 } 418 if (!left && size > 1U) { 419 *str++ = c; 420 size--; 421 } 422 } else { 423 count++; 424 if (size > 1U) { 425 *str++ = c; 426 size--; 427 } 428 } 429 break; 430 case 'p': 431 v = va_arg(ap, void *); 432 sprintf(buf, "%p", v); 433 length = strlen(buf); 434 if (precision > length) 435 zeropad = precision - length; 436 if (width > 0U) { 437 pad = width - length - zeropad; 438 if (pad < 0) 439 pad = 0; 440 } 441 count += length + pad + zeropad; 442 if (!left) 443 while (pad > 0 && size > 1U) { 444 *str++ = ' '; 445 size--; 446 pad--; 447 } 448 cp = buf; 449 if (zeropad > 0 && buf[0] == '0' && 450 (buf[1] == 'x' || buf[1] == 'X')) { 451 if (size > 1U) { 452 *str++ = *cp++; 453 size--; 454 } 455 if (size > 1U) { 456 *str++ = *cp++; 457 size--; 458 } 459 while (zeropad > 0 && size > 1U) { 460 *str++ = '0'; 461 size--; 462 zeropad--; 463 } 464 } 465 while (*cp != '\0' && size > 1U) { 466 *str++ = *cp++; 467 size--; 468 } 469 while (pad > 0 && size > 1U) { 470 *str++ = ' '; 471 size--; 472 pad--; 473 } 474 break; 475 case 'D': /*deprecated*/ 476 INSIST("use %ld instead of %D" == NULL); 477 case 'O': /*deprecated*/ 478 INSIST("use %lo instead of %O" == NULL); 479 case 'U': /*deprecated*/ 480 INSIST("use %lu instead of %U" == NULL); 481 482 case 'L': 483#ifdef HAVE_LONG_DOUBLE 484 l = 1; 485#else 486 INSIST("long doubles are not supported" == NULL); 487#endif 488 /*FALLTHROUGH*/ 489 case 'e': 490 case 'E': 491 case 'f': 492 case 'g': 493 case 'G': 494 if (!dot) 495 precision = 6; 496 /* 497 * IEEE floating point. 498 * MIN 2.2250738585072014E-308 499 * MAX 1.7976931348623157E+308 500 * VAX floating point has a smaller range than IEEE. 501 * 502 * precisions > 324 don't make much sense. 503 * if we cap the precision at 512 we will not 504 * overflow buf. 505 */ 506 if (precision > 512U) 507 precision = 512; 508 sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", 509 plus ? "+" : space ? " " : "", 510 precision, l ? "L" : "", *format); 511 switch (*format) { 512 case 'e': 513 case 'E': 514 case 'f': 515 case 'g': 516 case 'G': 517#ifdef HAVE_LONG_DOUBLE 518 if (l) { 519 ldbl = va_arg(ap, long double); 520 sprintf(buf, fmt, ldbl); 521 } else 522#endif 523 { 524 dbl = va_arg(ap, double); 525 sprintf(buf, fmt, dbl); 526 } 527 length = strlen(buf); 528 if (width > 0U) { 529 pad = width - length; 530 if (pad < 0) 531 pad = 0; 532 } 533 count += length + pad; 534 if (!left) 535 while (pad > 0 && size > 1U) { 536 *str++ = ' '; 537 size--; 538 pad--; 539 } 540 cp = buf; 541 while (*cp != ' ' && size > 1U) { 542 *str++ = *cp++; 543 size--; 544 } 545 while (pad > 0 && size > 1U) { 546 *str++ = ' '; 547 size--; 548 pad--; 549 } 550 break; 551 default: 552 continue; 553 } 554 break; 555 default: 556 continue; 557 } 558 format++; 559 } 560 if (size > 0U) 561 *str = '\0'; 562 return (count); 563} 564