mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-30 22:15:20 +00:00
Not all OS's yet supply {v}snprintf().
This implementation is nearly complete except for outragous precision values on floating point numbers. This impelemation has a maximum precision of 512.
This commit is contained in:
491
lib/isc/vsnprintf.c
Normal file
491
lib/isc/vsnprintf.c
Normal file
@@ -0,0 +1,491 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <isc/assertions.h>
|
||||
|
||||
int
|
||||
snprintf(char *str, size_t size, const char *format, ...) {
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
ret = vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
return (ret);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Return length of string that would have been written if not truncated.
|
||||
*/
|
||||
|
||||
int
|
||||
vsnprintf(char *str, size_t size, const char *format, va_list ap) {
|
||||
int h;
|
||||
int l;
|
||||
int q;
|
||||
int alt;
|
||||
int zero;
|
||||
int left;
|
||||
int plus;
|
||||
int space;
|
||||
int neg;
|
||||
long long tmpi;
|
||||
unsigned long long tmpui;
|
||||
unsigned long width;
|
||||
unsigned long precision;
|
||||
char buf[1024];
|
||||
char *cp;
|
||||
char *save = str;
|
||||
char c;
|
||||
void *v;
|
||||
char *head;
|
||||
int count = 0;
|
||||
int length;
|
||||
int pad;
|
||||
int zeropad;
|
||||
int dot;
|
||||
double dbl;
|
||||
long double ldbl;
|
||||
char fmt[32];
|
||||
|
||||
INSIST(str != NULL);
|
||||
INSIST(format != NULL);
|
||||
|
||||
while (*format != '\0') {
|
||||
if (*format != '%') {
|
||||
if (size > 1) {
|
||||
*str++ = *format;
|
||||
size--;
|
||||
}
|
||||
count++;
|
||||
format++;
|
||||
continue;
|
||||
}
|
||||
format++;
|
||||
|
||||
/* reset flags */
|
||||
dot = neg = space = plus = left = zero = alt = h = l = q = 0;
|
||||
width = precision = 0;
|
||||
head = "";
|
||||
length = pad = zeropad;
|
||||
|
||||
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(*format)) {
|
||||
char *e;
|
||||
width = strtoul(format, &e, 10);
|
||||
format = e;
|
||||
}
|
||||
|
||||
/* precision */
|
||||
if (*format == '.') {
|
||||
format++;
|
||||
dot = 1;
|
||||
if (*format == '*') {
|
||||
precision = va_arg(ap, int);
|
||||
format++;
|
||||
} else if (isdigit(*format)) {
|
||||
char *e;
|
||||
precision = strtoul(format, &e, 10);
|
||||
format = e;
|
||||
}
|
||||
}
|
||||
|
||||
switch (*format) {
|
||||
case '\0':
|
||||
continue;
|
||||
case '%':
|
||||
if (size > 1) {
|
||||
*str++ = *format;
|
||||
size--;
|
||||
}
|
||||
format++;
|
||||
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;
|
||||
goto doint;
|
||||
case 'n':
|
||||
case 'i':
|
||||
case 'd':
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
doint:
|
||||
if (precision != 0)
|
||||
zero = 0;
|
||||
switch (*format) {
|
||||
case 'n':
|
||||
if (h) {
|
||||
short int *p;
|
||||
p = va_arg(ap, short *);
|
||||
REQUIRE(p != NULL);
|
||||
*p = str - save;
|
||||
} else if (l) {
|
||||
long int *p;
|
||||
p = va_arg(ap, long *);
|
||||
REQUIRE(p != NULL);
|
||||
*p = str - save;
|
||||
} else {
|
||||
int *p;
|
||||
p = va_arg(ap, int *);
|
||||
REQUIRE(p != NULL);
|
||||
*p = str - save;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
case 'd':
|
||||
if (q)
|
||||
tmpi = va_arg(ap, long long);
|
||||
else if (l)
|
||||
tmpi = va_arg(ap, long int);
|
||||
else
|
||||
tmpi = va_arg(ap, int);
|
||||
if (tmpi < 0) {
|
||||
head = "-";
|
||||
tmpui = -tmpi;
|
||||
} else {
|
||||
if (plus)
|
||||
head = "+";
|
||||
else if (space)
|
||||
head = " ";
|
||||
else
|
||||
head = "";
|
||||
tmpui = tmpi;
|
||||
}
|
||||
sprintf(buf, "%llu", tmpui);
|
||||
goto printint;
|
||||
case 'o':
|
||||
if (q)
|
||||
tmpui = va_arg(ap, unsigned long long);
|
||||
else if (l)
|
||||
tmpi = va_arg(ap, long int);
|
||||
else
|
||||
tmpi = va_arg(ap, int);
|
||||
sprintf(buf, alt ? "%#llo" : "%llo", tmpui);
|
||||
goto printint;
|
||||
case 'u':
|
||||
if (q)
|
||||
tmpui = va_arg(ap, unsigned long long);
|
||||
else if (l)
|
||||
tmpui = va_arg(ap, unsigned long int);
|
||||
else
|
||||
tmpui = va_arg(ap, unsigned int);
|
||||
sprintf(buf, "%llu", tmpui);
|
||||
goto printint;
|
||||
case 'x':
|
||||
if (q)
|
||||
tmpui = va_arg(ap, unsigned long long);
|
||||
else if (l)
|
||||
tmpui = va_arg(ap, unsigned long int);
|
||||
else
|
||||
tmpui = va_arg(ap, unsigned int);
|
||||
if (alt) {
|
||||
head = "0x";
|
||||
if (precision > 2)
|
||||
precision -= 2;
|
||||
}
|
||||
sprintf(buf, "%llx", tmpui);
|
||||
goto printint;
|
||||
case 'X':
|
||||
if (q)
|
||||
tmpui = va_arg(ap, unsigned long long);
|
||||
else if (l)
|
||||
tmpui = va_arg(ap, unsigned long int);
|
||||
else
|
||||
tmpui = va_arg(ap, unsigned int);
|
||||
if (alt) {
|
||||
head = "0X";
|
||||
if (precision > 2)
|
||||
precision -= 2;
|
||||
}
|
||||
sprintf(buf, "%llX", tmpui);
|
||||
goto printint;
|
||||
printint:
|
||||
if (precision != 0 || width != 0) {
|
||||
length = strlen(buf);
|
||||
if (length < precision)
|
||||
zeropad = precision - length;
|
||||
if (width) {
|
||||
pad = width - length -
|
||||
zeropad - strlen(head);
|
||||
if (pad < 0)
|
||||
pad = 0;
|
||||
}
|
||||
}
|
||||
count += strlen(head) + strlen(buf) + pad +
|
||||
zeropad;
|
||||
if (!left) {
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
}
|
||||
cp = head;
|
||||
while (*cp != '\0' && size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
}
|
||||
while (zeropad > 0 && size > 1) {
|
||||
*str++ = '0';
|
||||
size--;
|
||||
zeropad--;
|
||||
}
|
||||
cp = buf;
|
||||
while (*cp != '\0' && size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
}
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
cp = va_arg(ap, char *);
|
||||
REQUIRE(cp != NULL);
|
||||
|
||||
if (precision != 0) {
|
||||
/* cp need not be NULL terminated */
|
||||
char *tp;
|
||||
unsigned long n;
|
||||
|
||||
n = precision;
|
||||
tp = cp;
|
||||
while (n != 0 && *tp != '0')
|
||||
n--, tp++;
|
||||
length = precision - n;
|
||||
} else {
|
||||
length = strlen(cp);
|
||||
}
|
||||
if (width != 0) {
|
||||
pad = width - length;
|
||||
if (pad < 0)
|
||||
pad = 0;
|
||||
}
|
||||
count += pad + length;
|
||||
if (!left)
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
if (precision != 0)
|
||||
while (precision > 0 && *cp != '\0' &&
|
||||
size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
precision--;
|
||||
}
|
||||
else
|
||||
while (*cp != '\0' && size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
}
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
c = va_arg(ap, int);
|
||||
if (width > 0) {
|
||||
count += width;
|
||||
width--;
|
||||
if (left) {
|
||||
*str++ = c;
|
||||
size--;
|
||||
}
|
||||
while (width-- > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
}
|
||||
if (!left && size > 1) {
|
||||
*str++ = c;
|
||||
size--;
|
||||
}
|
||||
} else {
|
||||
count++;
|
||||
if (size > 1) {
|
||||
*str++ = c;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
v = va_arg(ap, void *);
|
||||
sprintf(buf, "%p", v);
|
||||
length = strlen(buf);
|
||||
if (precision > length)
|
||||
zeropad = precision - length;
|
||||
if (width > 0) {
|
||||
pad = width - length - zeropad;
|
||||
if (pad < 0)
|
||||
pad = 0;
|
||||
}
|
||||
count += length + pad + zeropad;
|
||||
if (!left)
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
cp = buf;
|
||||
if (zeropad > 0 && buf[0] == '0' &&
|
||||
(buf[1] == 'x' || buf[1] == 'X')) {
|
||||
if (size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
}
|
||||
if (size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
}
|
||||
while (zeropad > 0 && size > 1) {
|
||||
*str++ = '0';
|
||||
size--;
|
||||
zeropad--;
|
||||
}
|
||||
}
|
||||
while (*cp != '\0' && size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
}
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
break;
|
||||
case 'D': /*deprecated*/
|
||||
INSIST("use %ld instead of %D" == NULL);
|
||||
case 'O': /*deprecated*/
|
||||
INSIST("use %lo instead of %O" == NULL);
|
||||
case 'U': /*deprecated*/
|
||||
INSIST("use %lo instead of %U" == NULL);
|
||||
|
||||
case 'L':
|
||||
l = 1;
|
||||
/*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 sence.
|
||||
* if we cap the precision at 512 we will not
|
||||
* overflow buf.
|
||||
*/
|
||||
if (precision > 512)
|
||||
precision = 512;
|
||||
sprintf(fmt, "%%%s%s.%d%s%c", alt ? "#" : "",
|
||||
plus ? "+" : space ? " " : "",
|
||||
precision, l ? "L" : "", *format);
|
||||
switch (*format) {
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'G':
|
||||
if (l) {
|
||||
ldbl = va_arg(ap, long double);
|
||||
sprintf(buf, fmt, ldbl);
|
||||
} else {
|
||||
dbl = va_arg(ap, double);
|
||||
sprintf(buf, fmt, dbl);
|
||||
}
|
||||
length = strlen(buf);
|
||||
if (width > 0) {
|
||||
pad = width - length;
|
||||
if (pad < 0)
|
||||
pad = 0;
|
||||
}
|
||||
count += length + pad;
|
||||
if (!left)
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
cp = buf;
|
||||
while (*cp != ' ' && size > 1) {
|
||||
*str++ = *cp++;
|
||||
size--;
|
||||
}
|
||||
while (pad > 0 && size > 1) {
|
||||
*str++ = ' ';
|
||||
size--;
|
||||
pad--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
format++;
|
||||
}
|
||||
if (size > 0)
|
||||
*str = '\0';
|
||||
return (count);
|
||||
}
|
Reference in New Issue
Block a user