1/* $NetBSD: print.c,v 1.6 2020/05/25 20:47:20 christos Exp $ */ 2 3/* 4 * Copyright (C) 2004-2008, 2010 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: print.c,v 1.37 2010/10/18 23:47:08 tbox Exp */ 21 22/*! \file */ 23 24#include <config.h> 25 26#include <ctype.h> 27#include <stdio.h> /* for sprintf() */ 28#include <string.h> /* for strlen() */ 29 30#define ISC__PRINT_SOURCE /* Used to get the isc_print_* prototypes. */ 31 32#include <isc/assertions.h> 33#include <isc/int.h> 34#include <isc/msgs.h> 35#include <isc/print.h> 36#include <isc/stdlib.h> 37#include <isc/util.h> 38 39int 40isc_print_sprintf(char *str, const char *format, ...) { 41 va_list ap; 42 43 va_start(ap, format); 44 vsprintf(str, format, ap); 45 va_end(ap); 46 return (strlen(str)); 47} 48 49/*! 50 * Return length of string that would have been written if not truncated. 51 */ 52 53int 54isc_print_snprintf(char *str, size_t size, const char *format, ...) { 55 va_list ap; 56 int ret; 57 58 va_start(ap, format); 59 ret = vsnprintf(str, size, format, ap); 60 va_end(ap); 61 return (ret); 62 63} 64 65/*! 66 * Return length of string that would have been written if not truncated. 67 */ 68 69int 70isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) { 71 int h; 72 int l; 73 int q; 74 int alt; 75 int zero; 76 int left; 77 int plus; 78 int space; 79 int neg; 80 isc_int64_t tmpi; 81 isc_uint64_t tmpui; 82 unsigned long width; 83 unsigned long precision; 84 unsigned int length; 85 char buf[1024]; 86 char c; 87 void *v; 88 char *save = str; 89 const char *cp; 90 const char *head; 91 int count = 0; 92 int pad; 93 int zeropad; 94 int dot; 95 double dbl; 96#ifdef HAVE_LONG_DOUBLE 97 long double ldbl; 98#endif 99 char fmt[32]; 100 101 INSIST(str != NULL); 102 INSIST(format != NULL); 103 104 while (*format != '\0') { 105 if (*format != '%') { 106 if (size > 1) { 107 *str++ = *format; 108 size--; 109 } 110 count++; 111 format++; 112 continue; 113 } 114 format++; 115 116 /* 117 * Reset flags. 118 */ 119 dot = neg = space = plus = left = zero = alt = h = l = q = 0; 120 width = precision = 0; 121 head = ""; 122 length = pad = zeropad = 0; 123 124 do { 125 if (*format == '#') { 126 alt = 1; 127 format++; 128 } else if (*format == '-') { 129 left = 1; 130 zero = 0; 131 format++; 132 } else if (*format == ' ') { 133 if (!plus) 134 space = 1; 135 format++; 136 } else if (*format == '+') { 137 plus = 1; 138 space = 0; 139 format++; 140 } else if (*format == '0') { 141 if (!left) 142 zero = 1; 143 format++; 144 } else 145 break; 146 } while (1); 147 148 /* 149 * Width. 150 */ 151 if (*format == '*') { 152 width = va_arg(ap, int); 153 format++; 154 } else if (isdigit((unsigned char)*format)) { 155 char *e; 156 width = strtoul(format, &e, 10); 157 format = e; 158 } 159 160 /* 161 * Precision. 162 */ 163 if (*format == '.') { 164 format++; 165 dot = 1; 166 if (*format == '*') { 167 precision = va_arg(ap, int); 168 format++; 169 } else if (isdigit((unsigned char)*format)) { 170 char *e; 171 precision = strtoul(format, &e, 10); 172 format = e; 173 } 174 } 175 176 switch (*format) { 177 case '\0': 178 continue; 179 case '%': 180 if (size > 1) { 181 *str++ = *format; 182 size--; 183 } 184 count++; 185 break; 186 case 'q': 187 q = 1; 188 format++; 189 goto doint; 190 case 'h': 191 h = 1; 192 format++; 193 goto doint; 194 case 'l': 195 l = 1; 196 format++; 197 if (*format == 'l') { 198 q = 1; 199 format++; 200 } 201 goto doint; 202 case 'n': 203 case 'i': 204 case 'd': 205 case 'o': 206 case 'u': 207 case 'x': 208 case 'X': 209 doint: 210 if (precision != 0) 211 zero = 0; 212 switch (*format) { 213 case 'n': 214 if (h) { 215 short int *p; 216 p = va_arg(ap, short *); 217 REQUIRE(p != NULL); 218 *p = str - save; 219 } else if (l) { 220 long int *p; 221 p = va_arg(ap, long *); 222 REQUIRE(p != NULL); 223 *p = str - save; 224 } else { 225 int *p; 226 p = va_arg(ap, int *); 227 REQUIRE(p != NULL); 228 *p = str - save; 229 } 230 break; 231 case 'i': 232 case 'd': 233 if (q) 234 tmpi = va_arg(ap, isc_int64_t); 235 else if (l) 236 tmpi = va_arg(ap, long int); 237 else 238 tmpi = va_arg(ap, int); 239 if (tmpi < 0) { 240 head = "-"; 241 tmpui = -tmpi; 242 } else { 243 if (plus) 244 head = "+"; 245 else if (space) 246 head = " "; 247 else 248 head = ""; 249 tmpui = tmpi; 250 } 251 if (tmpui <= 0xffffffffU) 252 sprintf(buf, "%lu", 253 (unsigned long)tmpui); 254 else { 255 unsigned long mid; 256 unsigned long lo; 257 unsigned long hi; 258 lo = tmpui % 1000000000; 259 tmpui /= 1000000000; 260 mid = tmpui % 1000000000; 261 hi = tmpui / 1000000000; 262 if (hi != 0) 263 sprintf(buf, "%lu", hi); 264 else 265 buf[0] = '\n'; 266 sprintf(buf + strlen(buf), "%lu", mid); 267 sprintf(buf + strlen(buf), "%lu", lo); 268 } 269 goto printint; 270 case 'o': 271 if (q) 272 tmpui = va_arg(ap, isc_uint64_t); 273 else if (l) 274 tmpui = va_arg(ap, long int); 275 else 276 tmpui = va_arg(ap, int); 277 if (tmpui <= 0xffffffffU) 278 sprintf(buf, alt ? "%#lo" : "%lo", 279 (unsigned long)tmpui); 280 else { 281 unsigned long mid; 282 unsigned long lo; 283 unsigned long hi; 284 lo = tmpui % 010000000000; 285 tmpui /= 010000000000; 286 mid = tmpui % 010000000000; 287 hi = tmpui / 010000000000; 288 if (hi != 0) { 289 sprintf(buf, 290 alt ? "%#lo" : "%lo", 291 hi); 292 sprintf(buf + strlen(buf), 293 "%lo", mid); 294 } else 295 sprintf(buf, 296 alt ? "%#lo" : "%lo", 297 mid); 298 sprintf(buf + strlen(buf), "%lo", lo); 299 } 300 goto printint; 301 case 'u': 302 if (q) 303 tmpui = va_arg(ap, isc_uint64_t); 304 else if (l) 305 tmpui = va_arg(ap, unsigned long int); 306 else 307 tmpui = va_arg(ap, unsigned int); 308 if (tmpui <= 0xffffffffU) 309 sprintf(buf, "%lu", 310 (unsigned long)tmpui); 311 else { 312 unsigned long mid; 313 unsigned long lo; 314 unsigned long hi; 315 lo = tmpui % 1000000000; 316 tmpui /= 1000000000; 317 mid = tmpui % 1000000000; 318 hi = tmpui / 1000000000; 319 if (hi != 0) 320 sprintf(buf, "%lu", hi); 321 else 322 buf[0] = '\n'; 323 sprintf(buf + strlen(buf), "%lu", mid); 324 sprintf(buf + strlen(buf), "%lu", lo); 325 } 326 goto printint; 327 case 'x': 328 if (q) 329 tmpui = va_arg(ap, isc_uint64_t); 330 else if (l) 331 tmpui = va_arg(ap, unsigned long int); 332 else 333 tmpui = va_arg(ap, unsigned int); 334 if (alt) { 335 head = "0x"; 336 if (precision > 2) 337 precision -= 2; 338 } 339 if (tmpui <= 0xffffffffU) 340 sprintf(buf, "%lx", 341 (unsigned long)tmpui); 342 else { 343 unsigned long hi = tmpui>>32; 344 unsigned long lo = tmpui & 0xffffffff; 345 sprintf(buf, "%lx", hi); 346 sprintf(buf + strlen(buf), "%lx", lo); 347 } 348 goto printint; 349 case 'X': 350 if (q) 351 tmpui = va_arg(ap, isc_uint64_t); 352 else if (l) 353 tmpui = va_arg(ap, unsigned long int); 354 else 355 tmpui = va_arg(ap, unsigned int); 356 if (alt) { 357 head = "0X"; 358 if (precision > 2) 359 precision -= 2; 360 } 361 if (tmpui <= 0xffffffffU) 362 sprintf(buf, "%lX", 363 (unsigned long)tmpui); 364 else { 365 unsigned long hi = tmpui>>32; 366 unsigned long lo = tmpui & 0xffffffff; 367 sprintf(buf, "%lX", hi); 368 sprintf(buf + strlen(buf), "%lX", lo); 369 } 370 goto printint; 371 printint: 372 if (precision != 0 || width != 0) { 373 length = strlen(buf); 374 if (length < precision) 375 zeropad = precision - length; 376 else if (length < width && zero) 377 zeropad = width - length; 378 if (width != 0) { 379 pad = width - length - 380 zeropad - strlen(head); 381 if (pad < 0) 382 pad = 0; 383 } 384 } 385 count += strlen(head) + strlen(buf) + pad + 386 zeropad; 387 if (!left) { 388 while (pad > 0 && size > 1) { 389 *str++ = ' '; 390 size--; 391 pad--; 392 } 393 } 394 cp = head; 395 while (*cp != '\0' && size > 1) { 396 *str++ = *cp++; 397 size--; 398 } 399 while (zeropad > 0 && size > 1) { 400 *str++ = '0'; 401 size--; 402 zeropad--; 403 } 404 cp = buf; 405 while (*cp != '\0' && size > 1) { 406 *str++ = *cp++; 407 size--; 408 } 409 while (pad > 0 && size > 1) { 410 *str++ = ' '; 411 size--; 412 pad--; 413 } 414 break; 415 default: 416 break; 417 } 418 break; 419 case 's': 420 cp = va_arg(ap, char *); 421 REQUIRE(cp != NULL); 422 423 if (precision != 0) { 424 /* 425 * cp need not be NULL terminated. 426 */ 427 const char *tp; 428 unsigned long n; 429 430 n = precision; 431 tp = cp; 432 while (n != 0 && *tp != '\0') 433 n--, tp++; 434 length = precision - n; 435 } else { 436 length = strlen(cp); 437 } 438 if (width != 0) { 439 pad = width - length; 440 if (pad < 0) 441 pad = 0; 442 } 443 count += pad + length; 444 if (!left) 445 while (pad > 0 && size > 1) { 446 *str++ = ' '; 447 size--; 448 pad--; 449 } 450 if (precision != 0) 451 while (precision > 0 && *cp != '\0' && 452 size > 1) { 453 *str++ = *cp++; 454 size--; 455 precision--; 456 } 457 else 458 while (*cp != '\0' && size > 1) { 459 *str++ = *cp++; 460 size--; 461 } 462 while (pad > 0 && size > 1) { 463 *str++ = ' '; 464 size--; 465 pad--; 466 } 467 break; 468 case 'c': 469 c = va_arg(ap, int); 470 if (width > 0) { 471 count += width; 472 width--; 473 if (left && size > 1) { 474 *str++ = c; 475 size--; 476 } 477 while (width-- > 0 && size > 1) { 478 *str++ = ' '; 479 size--; 480 } 481 if (!left && size > 1) { 482 *str++ = c; 483 size--; 484 } 485 } else { 486 count++; 487 if (size > 1) { 488 *str++ = c; 489 size--; 490 } 491 } 492 break; 493 case 'p': 494 v = va_arg(ap, void *); 495 sprintf(buf, "%p", v); 496 length = strlen(buf); 497 if (precision > length) 498 zeropad = precision - length; 499 if (width > 0) { 500 pad = width - length - zeropad; 501 if (pad < 0) 502 pad = 0; 503 } 504 count += length + pad + zeropad; 505 if (!left) 506 while (pad > 0 && size > 1) { 507 *str++ = ' '; 508 size--; 509 pad--; 510 } 511 cp = buf; 512 if (zeropad > 0 && buf[0] == '0' && 513 (buf[1] == 'x' || buf[1] == 'X')) { 514 if (size > 1) { 515 *str++ = *cp++; 516 size--; 517 } 518 if (size > 1) { 519 *str++ = *cp++; 520 size--; 521 } 522 while (zeropad > 0 && size > 1) { 523 *str++ = '0'; 524 size--; 525 zeropad--; 526 } 527 } 528 while (*cp != '\0' && size > 1) { 529 *str++ = *cp++; 530 size--; 531 } 532 while (pad > 0 && size > 1) { 533 *str++ = ' '; 534 size--; 535 pad--; 536 } 537 break; 538 case 'D': /*deprecated*/ 539 INSIST("use %ld instead of %D" == NULL); 540 case 'O': /*deprecated*/ 541 INSIST("use %lo instead of %O" == NULL); 542 case 'U': /*deprecated*/ 543 INSIST("use %lu instead of %U" == NULL); 544 545 case 'L': 546#ifdef HAVE_LONG_DOUBLE 547 l = 1; 548#else 549 INSIST("long doubles are not supported" == NULL); 550#endif 551 /*FALLTHROUGH*/ 552 case 'e': 553 case 'E': 554 case 'f': 555 case 'g': 556 case 'G': 557 if (!dot) 558 precision = 6; 559 /* 560 * IEEE floating point. 561 * MIN 2.2250738585072014E-308 562 * MAX 1.7976931348623157E+308 563 * VAX floating point has a smaller range than IEEE. 564 * 565 * precisions > 324 don't make much sense. 566 * if we cap the precision at 512 we will not 567 * overflow buf. 568 */ 569 if (precision > 512) 570 precision = 512; 571 sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", 572 plus ? "+" : space ? " " : "", 573 precision, l ? "L" : "", *format); 574 switch (*format) { 575 case 'e': 576 case 'E': 577 case 'f': 578 case 'g': 579 case 'G': 580#ifdef HAVE_LONG_DOUBLE 581 if (l) { 582 ldbl = va_arg(ap, long double); 583 sprintf(buf, fmt, ldbl); 584 } else 585#endif 586 { 587 dbl = va_arg(ap, double); 588 sprintf(buf, fmt, dbl); 589 } 590 length = strlen(buf); 591 if (width > 0) { 592 pad = width - length; 593 if (pad < 0) 594 pad = 0; 595 } 596 count += length + pad; 597 if (!left) 598 while (pad > 0 && size > 1) { 599 *str++ = ' '; 600 size--; 601 pad--; 602 } 603 cp = buf; 604 while (*cp != ' ' && size > 1) { 605 *str++ = *cp++; 606 size--; 607 } 608 while (pad > 0 && size > 1) { 609 *str++ = ' '; 610 size--; 611 pad--; 612 } 613 break; 614 default: 615 continue; 616 } 617 break; 618 default: 619 continue; 620 } 621 format++; 622 } 623 if (size > 0) 624 *str = '\0'; 625 return (count); 626} 627