/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include #include #include #include /* for sprintf() */ #include /* for strlen() */ #include /* for assert() */ #define ISC__PRINT_SOURCE /* Used to get the isc_print_* prototypes. */ #include #include #include #include #include /* * We use the system's sprintf so we undef it here. */ #undef sprintf static int isc__print_printf(void (*emit)(char, void *), void *arg, const char *format, va_list ap); static void file_emit(char c, void *arg) { FILE *fp = arg; int i = c & 0xff; putc(i, fp); } #if 0 static int isc_print_vfprintf(FILE *fp, const char *format, va_list ap) { assert(fp != NULL); assert(format != NULL); return (isc__print_printf(file_emit, fp, format, ap)); } #endif int isc_print_printf(const char *format, ...) { va_list ap; int n; assert(format != NULL); va_start(ap, format); n = isc__print_printf(file_emit, stdout, format, ap); va_end(ap); return (n); } int isc_print_fprintf(FILE *fp, const char *format, ...) { va_list ap; int n; assert(fp != NULL); assert(format != NULL); va_start(ap, format); n = isc__print_printf(file_emit, fp, format, ap); va_end(ap); return (n); } static void nocheck_emit(char c, void *arg) { struct { char *str; } *a = arg; *(a->str)++ = c; } int isc_print_sprintf(char *str, const char *format, ...) { struct { char *str; } arg; int n; va_list ap; arg.str = str; va_start(ap, format); n = isc__print_printf(nocheck_emit, &arg, format, ap); va_end(ap); return (n); } /*! * Return length of string that would have been written if not truncated. */ int isc_print_snprintf(char *str, size_t size, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = isc_print_vsnprintf(str, size, format, ap); va_end(ap); return (ret); } /*! * Return length of string that would have been written if not truncated. */ static void string_emit(char c, void *arg) { struct { char *str; size_t size; } *p = arg; if (p->size > 0U) { *(p->str)++ = c; p->size--; } } int isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) { struct { char *str; size_t size; } arg; int n; assert(str != NULL); assert(format != NULL); arg.str = str; arg.size = size; n = isc__print_printf(string_emit, &arg, format, ap); if (arg.size > 0U) *arg.str = '\0'; return (n); } static int isc__print_printf(void (*emit)(char, void *), void *arg, const char *format, va_list ap) { int h; int l; int z; int q; int alt; int zero; int left; int plus; int space; int64_t tmpi; uint64_t tmpui; unsigned long width; unsigned long precision; unsigned int length; char buf[1024]; char c; void *v; const char *cp; const char *head; int count = 0; int pad; int zeropad; int dot; double dbl; bool precision_set; #ifdef HAVE_LONG_DOUBLE long double ldbl; #endif char fmt[32]; assert(emit != NULL); assert(arg != NULL); assert(format != NULL); while (*format != '\0') { if (*format != '%') { emit(*format++, arg); count++; continue; } format++; /* * Reset flags. */ dot = space = plus = left = zero = alt = h = l = q = z = 0; width = precision = 0; head = ""; pad = zeropad = 0; precision_set = false; do { if (*format == '#') { alt = 1; format++; } else if (*format == '-') { left = 1; zero = 0; format++; } else if (*format == ' ') { if (!plus) space = 1; format++; } else if (*format == '+') { plus = 1; space = 0; format++; } else if (*format == '0') { if (!left) zero = 1; format++; } else break; } while (1); /* * Width. */ if (*format == '*') { width = va_arg(ap, int); format++; } else if (isdigit((unsigned char)*format)) { char *e; width = strtoul(format, &e, 10); format = e; } /* * Precision. */ if (*format == '.') { format++; dot = 1; if (*format == '*') { precision = va_arg(ap, int); precision_set = true; format++; } else if (isdigit((unsigned char)*format)) { char *e; precision = strtoul(format, &e, 10); precision_set = true; format = e; } } switch (*format) { case '\0': continue; case '%': emit(*format, arg); count++; break; case 'q': q = 1; format++; goto doint; case 'h': h = 1; format++; goto doint; case 'l': l = 1; format++; if (*format == 'l') { q = 1; format++; } goto doint; case 'z': z = 1; format++; goto doint; #ifdef WIN32 case 'I': /* Windows has I64 as a modifier for a quad. */ if (format[1] == '6' && format[2] == '4') { q = 1; format += 3; goto doint; } continue; #endif case 'n': case 'i': case 'd': case 'o': case 'u': case 'x': case 'X': doint: if (precision != 0U) zero = 0; switch (*format) { case 'n': if (h) { short int *p; p = va_arg(ap, short *); assert(p != NULL); *p = count; } else if (l) { long int *p; p = va_arg(ap, long *); assert(p != NULL); *p = count; } else if (z) { size_t *p; p = va_arg(ap, size_t *); assert(p != NULL); *p = count; } else { int *p; p = va_arg(ap, int *); assert(p != NULL); *p = count; } break; case 'i': case 'd': if (q) tmpi = va_arg(ap, int64_t); else if (l) tmpi = va_arg(ap, long int); else if (z) tmpi = va_arg(ap, ssize_t); else tmpi = va_arg(ap, int); if (tmpi < 0) { head = "-"; tmpui = -tmpi; } else { if (plus) head = "+"; else if (space) head = " "; else head = ""; tmpui = tmpi; } if (tmpui <= 0xffffffffU) sprintf(buf, "%lu", (unsigned long)tmpui); else { unsigned long mid; unsigned long lo; unsigned long hi; lo = tmpui % 1000000000; tmpui /= 1000000000; mid = tmpui % 1000000000; hi = tmpui / 1000000000; if (hi != 0U) { sprintf(buf, "%lu", hi); sprintf(buf + strlen(buf), "%09lu", mid); } else sprintf(buf, "%lu", mid); sprintf(buf + strlen(buf), "%09lu", lo); } goto printint; case 'o': if (q) tmpui = va_arg(ap, uint64_t); else if (l) tmpui = va_arg(ap, long int); else if (z) tmpui = va_arg(ap, size_t); else tmpui = va_arg(ap, int); if (tmpui <= 0xffffffffU) sprintf(buf, alt ? "%#lo" : "%lo", (unsigned long)tmpui); else { unsigned long mid; unsigned long lo; unsigned long hi; lo = tmpui % 010000000000; tmpui /= 010000000000; mid = tmpui % 010000000000; hi = tmpui / 010000000000; if (hi != 0U) { sprintf(buf, alt ? "%#lo" : "%lo", hi); sprintf(buf + strlen(buf), "%09lo", mid); } else sprintf(buf, alt ? "%#lo" : "%lo", mid); sprintf(buf + strlen(buf), "%09lo", lo); } goto printint; case 'u': if (q) tmpui = va_arg(ap, uint64_t); else if (l) tmpui = va_arg(ap, unsigned long int); else if (z) tmpui = va_arg(ap, size_t); else tmpui = va_arg(ap, unsigned int); if (tmpui <= 0xffffffffU) sprintf(buf, "%lu", (unsigned long)tmpui); else { unsigned long mid; unsigned long lo; unsigned long hi; lo = tmpui % 1000000000; tmpui /= 1000000000; mid = tmpui % 1000000000; hi = tmpui / 1000000000; if (hi != 0U) { sprintf(buf, "%lu", hi); sprintf(buf + strlen(buf), "%09lu", mid); } else sprintf(buf, "%lu", mid); sprintf(buf + strlen(buf), "%09lu", lo); } goto printint; case 'x': if (q) tmpui = va_arg(ap, uint64_t); else if (l) tmpui = va_arg(ap, unsigned long int); else if (z) tmpui = va_arg(ap, size_t); else tmpui = va_arg(ap, unsigned int); if (alt) { head = "0x"; if (precision > 2U) precision -= 2; } if (tmpui <= 0xffffffffU) sprintf(buf, "%lx", (unsigned long)tmpui); else { unsigned long hi = tmpui>>32; unsigned long lo = tmpui & 0xffffffff; sprintf(buf, "%lx", hi); sprintf(buf + strlen(buf), "%08lx", lo); } goto printint; case 'X': if (q) tmpui = va_arg(ap, uint64_t); else if (l) tmpui = va_arg(ap, unsigned long int); else if (z) tmpui = va_arg(ap, size_t); else tmpui = va_arg(ap, unsigned int); if (alt) { head = "0X"; if (precision > 2U) precision -= 2; } if (tmpui <= 0xffffffffU) sprintf(buf, "%lX", (unsigned long)tmpui); else { unsigned long hi = tmpui>>32; unsigned long lo = tmpui & 0xffffffff; sprintf(buf, "%lX", hi); sprintf(buf + strlen(buf), "%08lX", lo); } goto printint; printint: if (precision_set || width != 0U) { length = strlen(buf); if (length < precision) zeropad = precision - length; else if (length < width && zero) zeropad = width - length; if (width != 0U) { pad = width - length - zeropad - strlen(head); if (pad < 0) pad = 0; } } count += strlen(head) + strlen(buf) + pad + zeropad; if (!left) { while (pad > 0) { emit(' ', arg); pad--; } } cp = head; while (*cp != '\0') emit(*cp++, arg); while (zeropad > 0) { emit('0', arg); zeropad--; } cp = buf; while (*cp != '\0') emit(*cp++, arg); while (pad > 0) { emit(' ', arg); pad--; } break; default: break; } break; case 's': cp = va_arg(ap, char *); if (precision_set) { /* * cp need not be NULL terminated. */ const char *tp; unsigned long n; if (precision != 0U) assert(cp != NULL); n = precision; tp = cp; while (n != 0U && *tp != '\0') n--, tp++; length = precision - n; } else { assert(cp != NULL); length = strlen(cp); } if (width != 0U) { pad = width - length; if (pad < 0) pad = 0; } count += pad + length; if (!left) while (pad > 0) { emit(' ', arg); pad--; } if (precision_set) while (precision > 0U && *cp != '\0') { emit(*cp++, arg); precision--; } else while (*cp != '\0') emit(*cp++, arg); while (pad > 0) { emit(' ', arg); pad--; } break; case 'c': c = va_arg(ap, int); if (width > 0U) { count += width; width--; if (left) emit(c, arg); while (width-- > 0U) emit(' ', arg); if (!left) emit(c, arg); } else { count++; emit(c, arg); } break; case 'p': v = va_arg(ap, void *); sprintf(buf, "%p", v); length = strlen(buf); if (precision > length) zeropad = precision - length; if (width > 0U) { pad = width - length - zeropad; if (pad < 0) pad = 0; } count += length + pad + zeropad; if (!left) while (pad > 0) { emit(' ', arg); pad--; } cp = buf; if (zeropad > 0 && buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) { emit(*cp++, arg); emit(*cp++, arg); while (zeropad > 0) { emit('0', arg); zeropad--; } } while (*cp != '\0') emit(*cp++, arg); while (pad > 0) { emit(' ', arg); pad--; } break; case 'D': /*deprecated*/ /* cppcheck-suppress literalWithCharPtrCompare */ assert("use %ld instead of %D" == NULL); case 'O': /*deprecated*/ /* cppcheck-suppress literalWithCharPtrCompare */ assert("use %lo instead of %O" == NULL); case 'U': /*deprecated*/ /* cppcheck-suppress literalWithCharPtrCompare */ assert("use %lu instead of %U" == NULL); case 'L': #ifdef HAVE_LONG_DOUBLE l = 1; #else /* cppcheck-suppress literalWithCharPtrCompare */ assert("long doubles are not supported" == NULL); #endif /* FALLTHROUGH */ case 'e': case 'E': case 'f': case 'g': case 'G': if (!dot) precision = 6; /* * IEEE floating point. * MIN 2.2250738585072014E-308 * MAX 1.7976931348623157E+308 * VAX floating point has a smaller range than IEEE. * * precisions > 324 don't make much sense. * if we cap the precision at 512 we will not * overflow buf. */ if (precision > 512U) precision = 512; sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", plus ? "+" : space ? " " : "", precision, l ? "L" : "", *format); switch (*format) { case 'e': case 'E': case 'f': case 'g': case 'G': #ifdef HAVE_LONG_DOUBLE if (l) { ldbl = va_arg(ap, long double); sprintf(buf, fmt, ldbl); } else #endif { dbl = va_arg(ap, double); sprintf(buf, fmt, dbl); } length = strlen(buf); if (width > 0U) { pad = width - length; if (pad < 0) pad = 0; } count += length + pad; if (!left) while (pad > 0) { emit(' ', arg); pad--; } cp = buf; while (*cp != '\0') emit(*cp++, arg); while (pad > 0) { emit(' ', arg); pad--; } break; default: continue; } break; default: continue; } format++; } return (count); }