diff options
Diffstat (limited to '')
-rw-r--r-- | lib/isc/print.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/lib/isc/print.c b/lib/isc/print.c new file mode 100644 index 0000000..d4d4880 --- /dev/null +++ b/lib/isc/print.c @@ -0,0 +1,706 @@ +/* + * 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 <config.h> + +#include <ctype.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> /* for sprintf() */ +#include <string.h> /* for strlen() */ +#include <assert.h> /* for assert() */ + +#define ISC__PRINT_SOURCE /* Used to get the isc_print_* prototypes. */ + +#include <isc/assertions.h> +#include <isc/msgs.h> +#include <isc/print.h> +#include <isc/stdlib.h> +#include <isc/util.h> + +/* + * 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; + int neg; + 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 = neg = 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*/ + assert("use %ld instead of %D" == NULL); + case 'O': /*deprecated*/ + assert("use %lo instead of %O" == NULL); + case 'U': /*deprecated*/ + assert("use %lu instead of %U" == NULL); + + case 'L': +#ifdef HAVE_LONG_DOUBLE + l = 1; +#else + 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); +} |