1/* 2 * Copyright (C) 2004-2008, 2010 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2001, 2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id: print.c,v 1.37 2010/10/18 23:47:08 tbox Exp $ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <ctype.h> 25#include <stdio.h> /* for sprintf() */ 26#include <string.h> /* for strlen() */ 27 28#define ISC__PRINT_SOURCE /* Used to get the isc_print_* prototypes. */ 29 30#include <isc/assertions.h> 31#include <isc/int.h> 32#include <isc/msgs.h> 33#include <isc/print.h> 34#include <isc/stdlib.h> 35#include <isc/util.h> 36 37int 38isc_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 52isc_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 68isc_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 int neg; 78 isc_int64_t tmpi; 79 isc_uint64_t tmpui; 80 unsigned long width; 81 unsigned long precision; 82 unsigned int length; 83 char buf[1024]; 84 char c; 85 void *v; 86 char *save = str; 87 const char *cp; 88 const char *head; 89 int count = 0; 90 int pad; 91 int zeropad; 92 int dot; 93 double dbl; 94#ifdef HAVE_LONG_DOUBLE 95 long double ldbl; 96#endif 97 char fmt[32]; 98 99 INSIST(str != NULL); 100 INSIST(format != NULL); 101 102 while (*format != '\0') { 103 if (*format != '%') { 104 if (size > 1) { 105 *str++ = *format; 106 size--; 107 } 108 count++; 109 format++; 110 continue; 111 } 112 format++; 113 114 /* 115 * Reset flags. 116 */ 117 dot = neg = space = plus = left = zero = alt = h = l = q = 0; 118 width = precision = 0; 119 head = ""; 120 length = pad = zeropad = 0; 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 > 1) { 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 != 0) 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, isc_int64_t); 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 if (tmpui <= 0xffffffffU) 250 sprintf(buf, "%lu", 251 (unsigned long)tmpui); 252 else { 253 unsigned long mid; 254 unsigned long lo; 255 unsigned long hi; 256 lo = tmpui % 1000000000; 257 tmpui /= 1000000000; 258 mid = tmpui % 1000000000; 259 hi = tmpui / 1000000000; 260 if (hi != 0) 261 sprintf(buf, "%lu", hi); 262 else 263 buf[0] = '\n'; 264 sprintf(buf + strlen(buf), "%lu", mid); 265 sprintf(buf + strlen(buf), "%lu", lo); 266 } 267 goto printint; 268 case 'o': 269 if (q) 270 tmpui = va_arg(ap, isc_uint64_t); 271 else if (l) 272 tmpui = va_arg(ap, long int); 273 else 274 tmpui = va_arg(ap, int); 275 if (tmpui <= 0xffffffffU) 276 sprintf(buf, alt ? "%#lo" : "%lo", 277 (unsigned long)tmpui); 278 else { 279 unsigned long mid; 280 unsigned long lo; 281 unsigned long hi; 282 lo = tmpui % 010000000000; 283 tmpui /= 010000000000; 284 mid = tmpui % 010000000000; 285 hi = tmpui / 010000000000; 286 if (hi != 0) { 287 sprintf(buf, 288 alt ? "%#lo" : "%lo", 289 hi); 290 sprintf(buf + strlen(buf), 291 "%lo", mid); 292 } else 293 sprintf(buf, 294 alt ? "%#lo" : "%lo", 295 mid); 296 sprintf(buf + strlen(buf), "%lo", lo); 297 } 298 goto printint; 299 case 'u': 300 if (q) 301 tmpui = va_arg(ap, isc_uint64_t); 302 else if (l) 303 tmpui = va_arg(ap, unsigned long int); 304 else 305 tmpui = va_arg(ap, unsigned int); 306 if (tmpui <= 0xffffffffU) 307 sprintf(buf, "%lu", 308 (unsigned long)tmpui); 309 else { 310 unsigned long mid; 311 unsigned long lo; 312 unsigned long hi; 313 lo = tmpui % 1000000000; 314 tmpui /= 1000000000; 315 mid = tmpui % 1000000000; 316 hi = tmpui / 1000000000; 317 if (hi != 0) 318 sprintf(buf, "%lu", hi); 319 else 320 buf[0] = '\n'; 321 sprintf(buf + strlen(buf), "%lu", mid); 322 sprintf(buf + strlen(buf), "%lu", lo); 323 } 324 goto printint; 325 case 'x': 326 if (q) 327 tmpui = va_arg(ap, isc_uint64_t); 328 else if (l) 329 tmpui = va_arg(ap, unsigned long int); 330 else 331 tmpui = va_arg(ap, unsigned int); 332 if (alt) { 333 head = "0x"; 334 if (precision > 2) 335 precision -= 2; 336 } 337 if (tmpui <= 0xffffffffU) 338 sprintf(buf, "%lx", 339 (unsigned long)tmpui); 340 else { 341 unsigned long hi = tmpui>>32; 342 unsigned long lo = tmpui & 0xffffffff; 343 sprintf(buf, "%lx", hi); 344 sprintf(buf + strlen(buf), "%lx", lo); 345 } 346 goto printint; 347 case 'X': 348 if (q) 349 tmpui = va_arg(ap, isc_uint64_t); 350 else if (l) 351 tmpui = va_arg(ap, unsigned long int); 352 else 353 tmpui = va_arg(ap, unsigned int); 354 if (alt) { 355 head = "0X"; 356 if (precision > 2) 357 precision -= 2; 358 } 359 if (tmpui <= 0xffffffffU) 360 sprintf(buf, "%lX", 361 (unsigned long)tmpui); 362 else { 363 unsigned long hi = tmpui>>32; 364 unsigned long lo = tmpui & 0xffffffff; 365 sprintf(buf, "%lX", hi); 366 sprintf(buf + strlen(buf), "%lX", lo); 367 } 368 goto printint; 369 printint: 370 if (precision != 0 || width != 0) { 371 length = strlen(buf); 372 if (length < precision) 373 zeropad = precision - length; 374 else if (length < width && zero) 375 zeropad = width - length; 376 if (width != 0) { 377 pad = width - length - 378 zeropad - strlen(head); 379 if (pad < 0) 380 pad = 0; 381 } 382 } 383 count += strlen(head) + strlen(buf) + pad + 384 zeropad; 385 if (!left) { 386 while (pad > 0 && size > 1) { 387 *str++ = ' '; 388 size--; 389 pad--; 390 } 391 } 392 cp = head; 393 while (*cp != '\0' && size > 1) { 394 *str++ = *cp++; 395 size--; 396 } 397 while (zeropad > 0 && size > 1) { 398 *str++ = '0'; 399 size--; 400 zeropad--; 401 } 402 cp = buf; 403 while (*cp != '\0' && size > 1) { 404 *str++ = *cp++; 405 size--; 406 } 407 while (pad > 0 && size > 1) { 408 *str++ = ' '; 409 size--; 410 pad--; 411 } 412 break; 413 default: 414 break; 415 } 416 break; 417 case 's': 418 cp = va_arg(ap, char *); 419 REQUIRE(cp != NULL); 420 421 if (precision != 0) { 422 /* 423 * cp need not be NULL terminated. 424 */ 425 const char *tp; 426 unsigned long n; 427 428 n = precision; 429 tp = cp; 430 while (n != 0 && *tp != '\0') 431 n--, tp++; 432 length = precision - n; 433 } else { 434 length = strlen(cp); 435 } 436 if (width != 0) { 437 pad = width - length; 438 if (pad < 0) 439 pad = 0; 440 } 441 count += pad + length; 442 if (!left) 443 while (pad > 0 && size > 1) { 444 *str++ = ' '; 445 size--; 446 pad--; 447 } 448 if (precision != 0) 449 while (precision > 0 && *cp != '\0' && 450 size > 1) { 451 *str++ = *cp++; 452 size--; 453 precision--; 454 } 455 else 456 while (*cp != '\0' && size > 1) { 457 *str++ = *cp++; 458 size--; 459 } 460 while (pad > 0 && size > 1) { 461 *str++ = ' '; 462 size--; 463 pad--; 464 } 465 break; 466 case 'c': 467 c = va_arg(ap, int); 468 if (width > 0) { 469 count += width; 470 width--; 471 if (left && size > 1) { 472 *str++ = c; 473 size--; 474 } 475 while (width-- > 0 && size > 1) { 476 *str++ = ' '; 477 size--; 478 } 479 if (!left && size > 1) { 480 *str++ = c; 481 size--; 482 } 483 } else { 484 count++; 485 if (size > 1) { 486 *str++ = c; 487 size--; 488 } 489 } 490 break; 491 case 'p': 492 v = va_arg(ap, void *); 493 sprintf(buf, "%p", v); 494 length = strlen(buf); 495 if (precision > length) 496 zeropad = precision - length; 497 if (width > 0) { 498 pad = width - length - zeropad; 499 if (pad < 0) 500 pad = 0; 501 } 502 count += length + pad + zeropad; 503 if (!left) 504 while (pad > 0 && size > 1) { 505 *str++ = ' '; 506 size--; 507 pad--; 508 } 509 cp = buf; 510 if (zeropad > 0 && buf[0] == '0' && 511 (buf[1] == 'x' || buf[1] == 'X')) { 512 if (size > 1) { 513 *str++ = *cp++; 514 size--; 515 } 516 if (size > 1) { 517 *str++ = *cp++; 518 size--; 519 } 520 while (zeropad > 0 && size > 1) { 521 *str++ = '0'; 522 size--; 523 zeropad--; 524 } 525 } 526 while (*cp != '\0' && size > 1) { 527 *str++ = *cp++; 528 size--; 529 } 530 while (pad > 0 && size > 1) { 531 *str++ = ' '; 532 size--; 533 pad--; 534 } 535 break; 536 case 'D': /*deprecated*/ 537 INSIST("use %ld instead of %D" == NULL); 538 case 'O': /*deprecated*/ 539 INSIST("use %lo instead of %O" == NULL); 540 case 'U': /*deprecated*/ 541 INSIST("use %lu instead of %U" == NULL); 542 543 case 'L': 544#ifdef HAVE_LONG_DOUBLE 545 l = 1; 546#else 547 INSIST("long doubles are not supported" == NULL); 548#endif 549 /*FALLTHROUGH*/ 550 case 'e': 551 case 'E': 552 case 'f': 553 case 'g': 554 case 'G': 555 if (!dot) 556 precision = 6; 557 /* 558 * IEEE floating point. 559 * MIN 2.2250738585072014E-308 560 * MAX 1.7976931348623157E+308 561 * VAX floating point has a smaller range than IEEE. 562 * 563 * precisions > 324 don't make much sense. 564 * if we cap the precision at 512 we will not 565 * overflow buf. 566 */ 567 if (precision > 512) 568 precision = 512; 569 sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", 570 plus ? "+" : space ? " " : "", 571 precision, l ? "L" : "", *format); 572 switch (*format) { 573 case 'e': 574 case 'E': 575 case 'f': 576 case 'g': 577 case 'G': 578#ifdef HAVE_LONG_DOUBLE 579 if (l) { 580 ldbl = va_arg(ap, long double); 581 sprintf(buf, fmt, ldbl); 582 } else 583#endif 584 { 585 dbl = va_arg(ap, double); 586 sprintf(buf, fmt, dbl); 587 } 588 length = strlen(buf); 589 if (width > 0) { 590 pad = width - length; 591 if (pad < 0) 592 pad = 0; 593 } 594 count += length + pad; 595 if (!left) 596 while (pad > 0 && size > 1) { 597 *str++ = ' '; 598 size--; 599 pad--; 600 } 601 cp = buf; 602 while (*cp != ' ' && size > 1) { 603 *str++ = *cp++; 604 size--; 605 } 606 while (pad > 0 && size > 1) { 607 *str++ = ' '; 608 size--; 609 pad--; 610 } 611 break; 612 default: 613 continue; 614 } 615 break; 616 default: 617 continue; 618 } 619 format++; 620 } 621 if (size > 0) 622 *str = '\0'; 623 return (count); 624} 625