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