1135446Strhodes/* 2234010Sdougb * Copyright (C) 2004, 2005, 2007, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2001, 2003 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id$ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <ctype.h> 23135446Strhodes#include <stdio.h> /* for sprintf */ 24143731Sdougb#include <string.h> 25135446Strhodes 26135446Strhodes#define LWRES__PRINT_SOURCE /* Used to get the lwres_print_* prototypes. */ 27135446Strhodes 28153816Sdougb#include <lwres/stdlib.h> 29135446Strhodes 30135446Strhodes#include "assert_p.h" 31135446Strhodes#include "print_p.h" 32135446Strhodes 33153816Sdougb#define LWRES_PRINT_QUADFORMAT LWRES_PLATFORM_QUADFORMAT 34153816Sdougb 35135446Strhodesint 36135446Strhodeslwres__print_sprintf(char *str, const char *format, ...) { 37135446Strhodes va_list ap; 38135446Strhodes 39135446Strhodes va_start(ap, format); 40135446Strhodes vsprintf(str, format, ap); 41135446Strhodes va_end(ap); 42135446Strhodes return (strlen(str)); 43135446Strhodes} 44135446Strhodes 45135446Strhodes/* 46135446Strhodes * Return length of string that would have been written if not truncated. 47135446Strhodes */ 48135446Strhodes 49135446Strhodesint 50135446Strhodeslwres__print_snprintf(char *str, size_t size, const char *format, ...) { 51135446Strhodes va_list ap; 52135446Strhodes int ret; 53135446Strhodes 54135446Strhodes va_start(ap, format); 55135446Strhodes ret = vsnprintf(str, size, format, ap); 56135446Strhodes va_end(ap); 57135446Strhodes return (ret); 58135446Strhodes 59135446Strhodes} 60135446Strhodes 61135446Strhodes/* 62135446Strhodes * Return length of string that would have been written if not truncated. 63135446Strhodes */ 64135446Strhodes 65135446Strhodesint 66135446Strhodeslwres__print_vsnprintf(char *str, size_t size, const char *format, va_list ap) { 67135446Strhodes int h; 68135446Strhodes int l; 69135446Strhodes int q; 70135446Strhodes int alt; 71135446Strhodes int zero; 72135446Strhodes int left; 73135446Strhodes int plus; 74135446Strhodes int space; 75135446Strhodes long long tmpi; 76135446Strhodes unsigned long long tmpui; 77135446Strhodes unsigned long width; 78135446Strhodes unsigned long precision; 79135446Strhodes unsigned int length; 80135446Strhodes char buf[1024]; 81135446Strhodes char c; 82135446Strhodes void *v; 83135446Strhodes char *save = str; 84135446Strhodes const char *cp; 85135446Strhodes const char *head; 86135446Strhodes int count = 0; 87135446Strhodes int pad; 88135446Strhodes int zeropad; 89135446Strhodes int dot; 90135446Strhodes double dbl; 91135446Strhodes#ifdef HAVE_LONG_DOUBLE 92135446Strhodes long double ldbl; 93135446Strhodes#endif 94135446Strhodes char fmt[32]; 95135446Strhodes 96135446Strhodes INSIST(str != NULL); 97135446Strhodes INSIST(format != NULL); 98135446Strhodes 99135446Strhodes while (*format != '\0') { 100135446Strhodes if (*format != '%') { 101143731Sdougb if (size > 1U) { 102135446Strhodes *str++ = *format; 103135446Strhodes size--; 104135446Strhodes } 105135446Strhodes count++; 106135446Strhodes format++; 107135446Strhodes continue; 108135446Strhodes } 109135446Strhodes format++; 110135446Strhodes 111135446Strhodes /* 112135446Strhodes * Reset flags. 113135446Strhodes */ 114153816Sdougb dot = space = plus = left = zero = alt = h = l = q = 0; 115135446Strhodes width = precision = 0; 116135446Strhodes head = ""; 117135446Strhodes length = pad = zeropad = 0; 118225361Sdougb POST(length); 119135446Strhodes 120135446Strhodes do { 121135446Strhodes if (*format == '#') { 122135446Strhodes alt = 1; 123135446Strhodes format++; 124135446Strhodes } else if (*format == '-') { 125135446Strhodes left = 1; 126135446Strhodes zero = 0; 127135446Strhodes format++; 128135446Strhodes } else if (*format == ' ') { 129135446Strhodes if (!plus) 130135446Strhodes space = 1; 131135446Strhodes format++; 132135446Strhodes } else if (*format == '+') { 133135446Strhodes plus = 1; 134135446Strhodes space = 0; 135135446Strhodes format++; 136135446Strhodes } else if (*format == '0') { 137135446Strhodes if (!left) 138135446Strhodes zero = 1; 139135446Strhodes format++; 140135446Strhodes } else 141135446Strhodes break; 142135446Strhodes } while (1); 143135446Strhodes 144135446Strhodes /* 145135446Strhodes * Width. 146135446Strhodes */ 147135446Strhodes if (*format == '*') { 148135446Strhodes width = va_arg(ap, int); 149135446Strhodes format++; 150135446Strhodes } else if (isdigit((unsigned char)*format)) { 151135446Strhodes char *e; 152135446Strhodes width = strtoul(format, &e, 10); 153135446Strhodes format = e; 154135446Strhodes } 155135446Strhodes 156135446Strhodes /* 157135446Strhodes * Precision. 158135446Strhodes */ 159135446Strhodes if (*format == '.') { 160135446Strhodes format++; 161135446Strhodes dot = 1; 162135446Strhodes if (*format == '*') { 163135446Strhodes precision = va_arg(ap, int); 164135446Strhodes format++; 165135446Strhodes } else if (isdigit((unsigned char)*format)) { 166135446Strhodes char *e; 167135446Strhodes precision = strtoul(format, &e, 10); 168135446Strhodes format = e; 169135446Strhodes } 170135446Strhodes } 171135446Strhodes 172135446Strhodes switch (*format) { 173135446Strhodes case '\0': 174135446Strhodes continue; 175135446Strhodes case '%': 176143731Sdougb if (size > 1U) { 177135446Strhodes *str++ = *format; 178135446Strhodes size--; 179135446Strhodes } 180135446Strhodes count++; 181135446Strhodes break; 182135446Strhodes case 'q': 183135446Strhodes q = 1; 184135446Strhodes format++; 185135446Strhodes goto doint; 186135446Strhodes case 'h': 187135446Strhodes h = 1; 188135446Strhodes format++; 189135446Strhodes goto doint; 190135446Strhodes case 'l': 191135446Strhodes l = 1; 192135446Strhodes format++; 193135446Strhodes if (*format == 'l') { 194135446Strhodes q = 1; 195135446Strhodes format++; 196135446Strhodes } 197135446Strhodes goto doint; 198135446Strhodes case 'n': 199135446Strhodes case 'i': 200135446Strhodes case 'd': 201135446Strhodes case 'o': 202135446Strhodes case 'u': 203135446Strhodes case 'x': 204135446Strhodes case 'X': 205135446Strhodes doint: 206143731Sdougb if (precision != 0U) 207135446Strhodes zero = 0; 208135446Strhodes switch (*format) { 209135446Strhodes case 'n': 210135446Strhodes if (h) { 211135446Strhodes short int *p; 212135446Strhodes p = va_arg(ap, short *); 213135446Strhodes REQUIRE(p != NULL); 214135446Strhodes *p = str - save; 215135446Strhodes } else if (l) { 216135446Strhodes long int *p; 217135446Strhodes p = va_arg(ap, long *); 218135446Strhodes REQUIRE(p != NULL); 219135446Strhodes *p = str - save; 220135446Strhodes } else { 221135446Strhodes int *p; 222135446Strhodes p = va_arg(ap, int *); 223135446Strhodes REQUIRE(p != NULL); 224135446Strhodes *p = str - save; 225135446Strhodes } 226135446Strhodes break; 227135446Strhodes case 'i': 228135446Strhodes case 'd': 229135446Strhodes if (q) 230135446Strhodes tmpi = va_arg(ap, long long int); 231135446Strhodes else if (l) 232135446Strhodes tmpi = va_arg(ap, long int); 233135446Strhodes else 234135446Strhodes tmpi = va_arg(ap, int); 235135446Strhodes if (tmpi < 0) { 236135446Strhodes head = "-"; 237135446Strhodes tmpui = -tmpi; 238135446Strhodes } else { 239135446Strhodes if (plus) 240135446Strhodes head = "+"; 241135446Strhodes else if (space) 242135446Strhodes head = " "; 243135446Strhodes else 244135446Strhodes head = ""; 245135446Strhodes tmpui = tmpi; 246135446Strhodes } 247153816Sdougb sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u", 248135446Strhodes tmpui); 249135446Strhodes goto printint; 250135446Strhodes case 'o': 251135446Strhodes if (q) 252135446Strhodes tmpui = va_arg(ap, 253135446Strhodes unsigned long long int); 254135446Strhodes else if (l) 255135446Strhodes tmpui = va_arg(ap, long int); 256135446Strhodes else 257135446Strhodes tmpui = va_arg(ap, int); 258135446Strhodes sprintf(buf, 259153816Sdougb alt ? "%#" LWRES_PRINT_QUADFORMAT "o" 260153816Sdougb : "%" LWRES_PRINT_QUADFORMAT "o", 261153816Sdougb tmpui); 262135446Strhodes goto printint; 263135446Strhodes case 'u': 264135446Strhodes if (q) 265135446Strhodes tmpui = va_arg(ap, 266135446Strhodes unsigned long long int); 267135446Strhodes else if (l) 268135446Strhodes tmpui = va_arg(ap, unsigned long int); 269135446Strhodes else 270135446Strhodes tmpui = va_arg(ap, unsigned int); 271153816Sdougb sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u", 272153816Sdougb tmpui); 273135446Strhodes goto printint; 274135446Strhodes case 'x': 275135446Strhodes if (q) 276135446Strhodes tmpui = va_arg(ap, 277135446Strhodes unsigned long long int); 278135446Strhodes else if (l) 279135446Strhodes tmpui = va_arg(ap, unsigned long int); 280135446Strhodes else 281135446Strhodes tmpui = va_arg(ap, unsigned int); 282135446Strhodes if (alt) { 283135446Strhodes head = "0x"; 284143731Sdougb if (precision > 2U) 285135446Strhodes precision -= 2; 286135446Strhodes } 287153816Sdougb sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "x", 288153816Sdougb tmpui); 289135446Strhodes goto printint; 290135446Strhodes case 'X': 291135446Strhodes if (q) 292135446Strhodes tmpui = va_arg(ap, 293135446Strhodes unsigned long long int); 294135446Strhodes else if (l) 295135446Strhodes tmpui = va_arg(ap, unsigned long int); 296135446Strhodes else 297135446Strhodes tmpui = va_arg(ap, unsigned int); 298135446Strhodes if (alt) { 299135446Strhodes head = "0X"; 300143731Sdougb if (precision > 2U) 301135446Strhodes precision -= 2; 302135446Strhodes } 303153816Sdougb sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "X", 304153816Sdougb tmpui); 305135446Strhodes goto printint; 306135446Strhodes printint: 307143731Sdougb if (precision != 0U || width != 0U) { 308135446Strhodes length = strlen(buf); 309135446Strhodes if (length < precision) 310135446Strhodes zeropad = precision - length; 311135446Strhodes else if (length < width && zero) 312135446Strhodes zeropad = width - length; 313143731Sdougb if (width != 0U) { 314135446Strhodes pad = width - length - 315135446Strhodes zeropad - strlen(head); 316135446Strhodes if (pad < 0) 317135446Strhodes pad = 0; 318135446Strhodes } 319135446Strhodes } 320135446Strhodes count += strlen(head) + strlen(buf) + pad + 321135446Strhodes zeropad; 322135446Strhodes if (!left) { 323143731Sdougb while (pad > 0 && size > 1U) { 324135446Strhodes *str++ = ' '; 325135446Strhodes size--; 326135446Strhodes pad--; 327135446Strhodes } 328135446Strhodes } 329135446Strhodes cp = head; 330143731Sdougb while (*cp != '\0' && size > 1U) { 331135446Strhodes *str++ = *cp++; 332135446Strhodes size--; 333135446Strhodes } 334143731Sdougb while (zeropad > 0 && size > 1U) { 335135446Strhodes *str++ = '0'; 336135446Strhodes size--; 337135446Strhodes zeropad--; 338135446Strhodes } 339135446Strhodes cp = buf; 340143731Sdougb while (*cp != '\0' && size > 1U) { 341135446Strhodes *str++ = *cp++; 342135446Strhodes size--; 343135446Strhodes } 344143731Sdougb while (pad > 0 && size > 1U) { 345135446Strhodes *str++ = ' '; 346135446Strhodes size--; 347135446Strhodes pad--; 348135446Strhodes } 349135446Strhodes break; 350135446Strhodes default: 351135446Strhodes break; 352135446Strhodes } 353135446Strhodes break; 354135446Strhodes case 's': 355135446Strhodes cp = va_arg(ap, char *); 356135446Strhodes REQUIRE(cp != NULL); 357135446Strhodes 358143731Sdougb if (precision != 0U) { 359135446Strhodes /* 360135446Strhodes * cp need not be NULL terminated. 361135446Strhodes */ 362135446Strhodes const char *tp; 363135446Strhodes unsigned long n; 364135446Strhodes 365135446Strhodes n = precision; 366135446Strhodes tp = cp; 367143731Sdougb while (n != 0U && *tp != '\0') 368135446Strhodes n--, tp++; 369135446Strhodes length = precision - n; 370135446Strhodes } else { 371135446Strhodes length = strlen(cp); 372135446Strhodes } 373143731Sdougb if (width != 0U) { 374135446Strhodes pad = width - length; 375135446Strhodes if (pad < 0) 376135446Strhodes pad = 0; 377135446Strhodes } 378135446Strhodes count += pad + length; 379135446Strhodes if (!left) 380143731Sdougb while (pad > 0 && size > 1U) { 381135446Strhodes *str++ = ' '; 382135446Strhodes size--; 383135446Strhodes pad--; 384135446Strhodes } 385143731Sdougb if (precision != 0U) 386143731Sdougb while (precision > 0U && *cp != '\0' && 387143731Sdougb size > 1U) { 388135446Strhodes *str++ = *cp++; 389135446Strhodes size--; 390135446Strhodes precision--; 391135446Strhodes } 392135446Strhodes else 393143731Sdougb while (*cp != '\0' && size > 1U) { 394135446Strhodes *str++ = *cp++; 395135446Strhodes size--; 396135446Strhodes } 397143731Sdougb while (pad > 0 && size > 1U) { 398135446Strhodes *str++ = ' '; 399135446Strhodes size--; 400135446Strhodes pad--; 401135446Strhodes } 402135446Strhodes break; 403135446Strhodes case 'c': 404135446Strhodes c = va_arg(ap, int); 405143731Sdougb if (width > 0U) { 406135446Strhodes count += width; 407135446Strhodes width--; 408135446Strhodes if (left) { 409135446Strhodes *str++ = c; 410135446Strhodes size--; 411135446Strhodes } 412143731Sdougb while (width-- > 0U && size > 1U) { 413135446Strhodes *str++ = ' '; 414135446Strhodes size--; 415135446Strhodes } 416143731Sdougb if (!left && size > 1U) { 417135446Strhodes *str++ = c; 418135446Strhodes size--; 419135446Strhodes } 420135446Strhodes } else { 421135446Strhodes count++; 422143731Sdougb if (size > 1U) { 423135446Strhodes *str++ = c; 424135446Strhodes size--; 425135446Strhodes } 426135446Strhodes } 427135446Strhodes break; 428135446Strhodes case 'p': 429135446Strhodes v = va_arg(ap, void *); 430135446Strhodes sprintf(buf, "%p", v); 431135446Strhodes length = strlen(buf); 432135446Strhodes if (precision > length) 433135446Strhodes zeropad = precision - length; 434143731Sdougb if (width > 0U) { 435135446Strhodes pad = width - length - zeropad; 436135446Strhodes if (pad < 0) 437135446Strhodes pad = 0; 438135446Strhodes } 439135446Strhodes count += length + pad + zeropad; 440135446Strhodes if (!left) 441143731Sdougb while (pad > 0 && size > 1U) { 442135446Strhodes *str++ = ' '; 443135446Strhodes size--; 444135446Strhodes pad--; 445135446Strhodes } 446135446Strhodes cp = buf; 447135446Strhodes if (zeropad > 0 && buf[0] == '0' && 448135446Strhodes (buf[1] == 'x' || buf[1] == 'X')) { 449143731Sdougb if (size > 1U) { 450135446Strhodes *str++ = *cp++; 451135446Strhodes size--; 452135446Strhodes } 453143731Sdougb if (size > 1U) { 454135446Strhodes *str++ = *cp++; 455135446Strhodes size--; 456135446Strhodes } 457143731Sdougb while (zeropad > 0 && size > 1U) { 458135446Strhodes *str++ = '0'; 459135446Strhodes size--; 460135446Strhodes zeropad--; 461135446Strhodes } 462135446Strhodes } 463143731Sdougb while (*cp != '\0' && size > 1U) { 464135446Strhodes *str++ = *cp++; 465135446Strhodes size--; 466135446Strhodes } 467143731Sdougb while (pad > 0 && size > 1U) { 468135446Strhodes *str++ = ' '; 469135446Strhodes size--; 470135446Strhodes pad--; 471135446Strhodes } 472135446Strhodes break; 473254402Serwin 474135446Strhodes case 'D': /*deprecated*/ 475135446Strhodes INSIST("use %ld instead of %D" == NULL); 476254402Serwin break; 477135446Strhodes case 'O': /*deprecated*/ 478135446Strhodes INSIST("use %lo instead of %O" == NULL); 479254402Serwin break; 480135446Strhodes case 'U': /*deprecated*/ 481135446Strhodes INSIST("use %lu instead of %U" == NULL); 482254402Serwin break; 483135446Strhodes 484135446Strhodes case 'L': 485135446Strhodes#ifdef HAVE_LONG_DOUBLE 486135446Strhodes l = 1; 487135446Strhodes#else 488135446Strhodes INSIST("long doubles are not supported" == NULL); 489135446Strhodes#endif 490135446Strhodes /*FALLTHROUGH*/ 491135446Strhodes case 'e': 492135446Strhodes case 'E': 493135446Strhodes case 'f': 494135446Strhodes case 'g': 495135446Strhodes case 'G': 496135446Strhodes if (!dot) 497135446Strhodes precision = 6; 498135446Strhodes /* 499135446Strhodes * IEEE floating point. 500135446Strhodes * MIN 2.2250738585072014E-308 501135446Strhodes * MAX 1.7976931348623157E+308 502135446Strhodes * VAX floating point has a smaller range than IEEE. 503135446Strhodes * 504135446Strhodes * precisions > 324 don't make much sense. 505135446Strhodes * if we cap the precision at 512 we will not 506135446Strhodes * overflow buf. 507135446Strhodes */ 508143731Sdougb if (precision > 512U) 509135446Strhodes precision = 512; 510135446Strhodes sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", 511135446Strhodes plus ? "+" : space ? " " : "", 512135446Strhodes precision, l ? "L" : "", *format); 513135446Strhodes switch (*format) { 514135446Strhodes case 'e': 515135446Strhodes case 'E': 516135446Strhodes case 'f': 517135446Strhodes case 'g': 518135446Strhodes case 'G': 519135446Strhodes#ifdef HAVE_LONG_DOUBLE 520135446Strhodes if (l) { 521135446Strhodes ldbl = va_arg(ap, long double); 522135446Strhodes sprintf(buf, fmt, ldbl); 523135446Strhodes } else 524135446Strhodes#endif 525135446Strhodes { 526135446Strhodes dbl = va_arg(ap, double); 527135446Strhodes sprintf(buf, fmt, dbl); 528135446Strhodes } 529135446Strhodes length = strlen(buf); 530143731Sdougb if (width > 0U) { 531135446Strhodes pad = width - length; 532135446Strhodes if (pad < 0) 533135446Strhodes pad = 0; 534135446Strhodes } 535135446Strhodes count += length + pad; 536135446Strhodes if (!left) 537143731Sdougb while (pad > 0 && size > 1U) { 538135446Strhodes *str++ = ' '; 539135446Strhodes size--; 540135446Strhodes pad--; 541135446Strhodes } 542135446Strhodes cp = buf; 543143731Sdougb while (*cp != ' ' && size > 1U) { 544135446Strhodes *str++ = *cp++; 545135446Strhodes size--; 546135446Strhodes } 547143731Sdougb while (pad > 0 && size > 1U) { 548135446Strhodes *str++ = ' '; 549135446Strhodes size--; 550135446Strhodes pad--; 551135446Strhodes } 552135446Strhodes break; 553135446Strhodes default: 554135446Strhodes continue; 555135446Strhodes } 556135446Strhodes break; 557135446Strhodes default: 558135446Strhodes continue; 559135446Strhodes } 560135446Strhodes format++; 561135446Strhodes } 562143731Sdougb if (size > 0U) 563135446Strhodes *str = '\0'; 564135446Strhodes return (count); 565135446Strhodes} 566