mirror of
https://github.com/NishiOwO/ircservices5.git
synced 2025-04-21 08:44:38 +00:00
558 lines
18 KiB
C
558 lines
18 KiB
C
/* An implementation of vsnprintf() for systems that don't have it.
|
|
*
|
|
* IRC Services is copyright (c) 1996-2009 Andrew Church.
|
|
* E-mail: <achurch@achurch.org>
|
|
* Parts written by Andrew Kempe and others.
|
|
* This program is free but copyrighted software; see the file GPL.txt for
|
|
* details.
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
typedef int
|
|
(*_pfmt_writefunc_t)(const char *buf, size_t len, void *arg1, void *arg2);
|
|
|
|
int my_vsnprintf(char *string, size_t size, const char *format, va_list args);
|
|
int my_snprintf(char *string, size_t size, const char *format, ...);
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Basic format routine for *printf() interfaces. Takes a writing function
|
|
* and two (optional) pointer parameters for that function which are passed
|
|
* on unmodified. The function should return the number of bytes written,
|
|
* or 0 (not -1!) on write failure.
|
|
*/
|
|
|
|
static int _pfmt(const char *format, va_list args,
|
|
_pfmt_writefunc_t writefunc, void *arg1, void *arg2)
|
|
{
|
|
int total = 0; /* Total bytes written */
|
|
const char *startptr;/* Beginning of non-token text in format string.
|
|
* Used for writing in bulk instead of
|
|
* character-at-a-time. */
|
|
int n; /* Bytes written in last writefunc() call */
|
|
int valid; /* Was this a valid %-token? */
|
|
int alt_form; /* "Alternate form"? (# flag) */
|
|
int zero_pad; /* Zero-pad value? */
|
|
int left_justify; /* Left-justify? (0 means right-justify) */
|
|
int always_sign; /* Always add sign value? */
|
|
int insert_blank; /* Insert blank before positive values for %d/%i? */
|
|
int width; /* Field width */
|
|
int precision; /* Precision */
|
|
int argsize; /* Size of argument: 0=normal, 1=short, 2=long,
|
|
* 3=long long */
|
|
int what; /* What are we working on? 0=flags, 1=width,
|
|
* 2=precision, 3=argsize, 4=argtype */
|
|
long intval; /* Integer value */
|
|
char *strval; /* String value */
|
|
void *ptrval; /* Pointer value */
|
|
char numbuf[64]; /* Temporary buffer for printing numbers; this MUST
|
|
* be large enough to hold the longest possible
|
|
* number (size is not checked in processing) */
|
|
char *numptr; /* Pointer to start of printed number in `numbuf' */
|
|
|
|
|
|
intval = 0;
|
|
strval = NULL;
|
|
ptrval = NULL;
|
|
|
|
startptr = format;
|
|
while (*format) {
|
|
if (*format != '%') {
|
|
format++;
|
|
continue;
|
|
}
|
|
if (startptr != format) {
|
|
/* Write out accumulated text */
|
|
n = writefunc(startptr, format-startptr, arg1, arg2);
|
|
total += n;
|
|
/* Abort on short write */
|
|
if (n != format-startptr)
|
|
break;
|
|
/* Point to this token, in case it's a bad one */
|
|
startptr = format;
|
|
}
|
|
|
|
/* Begin %-token processing */
|
|
valid = 0; /* 1 if valid, -1 if known not valid (syntax error) */
|
|
alt_form = 0;
|
|
left_justify = 0;
|
|
always_sign = 0;
|
|
insert_blank = 0;
|
|
zero_pad = 0;
|
|
width = -1;
|
|
precision = -1;
|
|
argsize = 0;
|
|
what = 0;
|
|
|
|
while (!valid && *++format) { /* Broken out of by terminal chars */
|
|
switch (*format) {
|
|
|
|
/* Flags */
|
|
case '#':
|
|
if (what != 0) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
alt_form = 1;
|
|
break;
|
|
case '-':
|
|
if (what != 0) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
left_justify = 1;
|
|
break;
|
|
case '+':
|
|
if (what != 0) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
always_sign = 1;
|
|
break;
|
|
case ' ':
|
|
if (what != 0) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
insert_blank = 1;
|
|
break;
|
|
case '\'':
|
|
/* Comma-grouping by locale; not supported */
|
|
valid = -1;
|
|
break;
|
|
case '0':
|
|
if (what == 0) {
|
|
zero_pad = 1;
|
|
break;
|
|
}
|
|
/* else fall through */
|
|
|
|
/* Field widths */
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
if (what == 0)
|
|
what = 1;
|
|
else if (what > 2) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
if (what == 1) {
|
|
if (width < 0)
|
|
width = 0;
|
|
width = width*10 + (*format)-'0';
|
|
} else {
|
|
if (precision < 0)
|
|
precision = 0;
|
|
precision = precision*10 + (*format)-'0';
|
|
}
|
|
break;
|
|
case '*':
|
|
if (what == 0)
|
|
what = 1;
|
|
else if (what > 2) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
if (what == 1) {
|
|
width = va_arg(args, int);
|
|
if (width < 0) {
|
|
width = -width;
|
|
left_justify = 1;
|
|
}
|
|
} else {
|
|
precision = va_arg(args, int);
|
|
}
|
|
break;
|
|
case '.':
|
|
if (what >= 2) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
what = 2;
|
|
break;
|
|
|
|
/* Argument sizes */
|
|
case 'h':
|
|
if (what > 3) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
argsize = 1;
|
|
what = 4;
|
|
break;
|
|
case 'l':
|
|
if (what > 3) {
|
|
valid = -1;
|
|
break;
|
|
}
|
|
argsize = 2;
|
|
what = 4;
|
|
break;
|
|
case 'L':
|
|
/* Long long (64 bits); not supported */
|
|
valid = -1;
|
|
break;
|
|
/* Argument types */
|
|
case 'd':
|
|
case 'i':
|
|
case 'o':
|
|
case 'u':
|
|
case 'x':
|
|
case 'X':
|
|
if (argsize == 1)
|
|
intval = va_arg(args, int);
|
|
else if (argsize == 2)
|
|
intval = va_arg(args, long);
|
|
else
|
|
intval = va_arg(args, int);
|
|
valid = 1;
|
|
break;
|
|
case 'c':
|
|
intval = va_arg(args, int);
|
|
valid = 1;
|
|
break;
|
|
case 's':
|
|
strval = va_arg(args, char *);
|
|
valid = 1;
|
|
break;
|
|
case 'p':
|
|
ptrval = va_arg(args, void *);
|
|
valid = 1;
|
|
break;
|
|
case 'n':
|
|
*((int *)va_arg(args, int *)) = total;
|
|
valid = 1;
|
|
break;
|
|
case '%':
|
|
valid = 1;
|
|
break;
|
|
|
|
/* All other characters */
|
|
default:
|
|
valid = -1;
|
|
break;
|
|
} /* switch (*format) */
|
|
}
|
|
if (valid != 1) {
|
|
/* Not a valid %-token; start loop over (token will get printed
|
|
* out next time through). */
|
|
continue;
|
|
}
|
|
|
|
/* Don't zero-pad if a precision was given or left-justifying */
|
|
if (precision != -1 || left_justify)
|
|
zero_pad = 0;
|
|
|
|
/* For numbers, limit precision to the size of the print buffer */
|
|
if ((*format=='d' || *format=='i' || *format=='o' || *format=='u'
|
|
|| *format=='x' || *format=='X')
|
|
&& precision > (signed) sizeof(numbuf))
|
|
{
|
|
precision = sizeof(numbuf);
|
|
}
|
|
|
|
switch (*format++) { /* Do something with this token */
|
|
case '%':
|
|
(*writefunc)(format-1, 1, arg1, arg2);
|
|
total++;
|
|
break;
|
|
|
|
case 'p':
|
|
/* Print the NULL value specially */
|
|
if (ptrval == NULL) {
|
|
strval = "(null)";
|
|
goto handle_string;
|
|
}
|
|
/* For all other values, pretend it's really %#.8x */
|
|
alt_form = 1;
|
|
zero_pad = 0;
|
|
precision = 8;
|
|
intval = (long) ptrval;
|
|
/* Fall through */
|
|
|
|
case 'x':
|
|
case 'X': {
|
|
static const char x_chars[] = "0123456789abcdef0x";
|
|
static const char X_chars[] = "0123456789ABCDEF0X";
|
|
const char *chars = (format[-1]=='X') ? X_chars : x_chars;
|
|
const char *padstr = zero_pad ? "0" : " ";
|
|
unsigned long uintval;
|
|
int len;
|
|
|
|
uintval = (unsigned long) intval;
|
|
if (alt_form && uintval != 0) {
|
|
n = writefunc(chars+16, 2, arg1, arg2);
|
|
total += n;
|
|
if (n != 2)
|
|
break;
|
|
width -= 2;
|
|
}
|
|
if (precision < 1)
|
|
precision = 1;
|
|
numptr = numbuf + sizeof(numbuf);
|
|
for (len = 0; len < precision || uintval != 0; len++) {
|
|
*--numptr = chars[uintval%16];
|
|
uintval /= 16;
|
|
}
|
|
if (left_justify) {
|
|
n = writefunc(numptr, len, arg1, arg2);
|
|
total += n;
|
|
if (n != len)
|
|
break;
|
|
}
|
|
while (len < width) {
|
|
if (1 != writefunc(padstr, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
width--;
|
|
}
|
|
if (!left_justify)
|
|
total += writefunc(numptr, len, arg1, arg2);
|
|
break;
|
|
} /* case 'x', 'X' */
|
|
|
|
case 'o': {
|
|
const char *padstr = zero_pad ? "0" : " ";
|
|
unsigned long uintval;
|
|
int len;
|
|
|
|
uintval = (unsigned long) intval;
|
|
if (precision < 1)
|
|
precision = 1;
|
|
numptr = numbuf + sizeof(numbuf);
|
|
for (len = 0; len < precision || uintval != 0; len++) {
|
|
*--numptr = '0' + uintval%8;
|
|
uintval /= 8;
|
|
}
|
|
if (alt_form && *numptr != '0') {
|
|
*--numptr = '0';
|
|
len++;
|
|
}
|
|
if (left_justify) {
|
|
n = writefunc(numptr, len, arg1, arg2);
|
|
total += n;
|
|
if (n != len)
|
|
break;
|
|
}
|
|
while (len < width) {
|
|
if (1 != writefunc(padstr, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
width--;
|
|
}
|
|
if (!left_justify)
|
|
total += writefunc(numptr, len, arg1, arg2);
|
|
break;
|
|
} /* case 'o' */
|
|
|
|
case 'u': {
|
|
const char *padstr = zero_pad ? "0" : " ";
|
|
unsigned long uintval;
|
|
int len;
|
|
|
|
uintval = (unsigned long) intval;
|
|
if (precision < 1)
|
|
precision = 1;
|
|
numptr = numbuf + sizeof(numbuf);
|
|
for (len = 0; len < precision || uintval != 0; len++) {
|
|
*--numptr = '0' + uintval%10;
|
|
uintval /= 10;
|
|
}
|
|
if (left_justify) {
|
|
n = writefunc(numptr, len, arg1, arg2);
|
|
total += n;
|
|
if (n != len)
|
|
break;
|
|
}
|
|
while (len < width) {
|
|
if (1 != writefunc(padstr, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
width--;
|
|
}
|
|
if (!left_justify)
|
|
total += writefunc(numptr, len, arg1, arg2);
|
|
break;
|
|
} /* case 'u' */
|
|
|
|
case 'd':
|
|
case 'i': {
|
|
const char *padstr = zero_pad ? "0" : " ";
|
|
int len;
|
|
char sign_char;
|
|
|
|
numptr = numbuf + sizeof(numbuf);
|
|
len = 0;
|
|
sign_char = 0;
|
|
if (intval < 0) {
|
|
sign_char = '-';
|
|
intval = -intval;
|
|
if (intval < 0) { /* true for 0x800...0 */
|
|
*numptr-- = '0' - intval%10;
|
|
len++;
|
|
intval /= 10;
|
|
intval = -intval;
|
|
}
|
|
}
|
|
if (precision < 1)
|
|
precision = 1;
|
|
for (; len < precision || intval != 0; len++) {
|
|
*--numptr = '0' + intval%10;
|
|
intval /= 10;
|
|
}
|
|
if (!sign_char) {
|
|
if (always_sign)
|
|
sign_char = '+';
|
|
else if (insert_blank)
|
|
sign_char = ' ';
|
|
}
|
|
if (sign_char) {
|
|
if (zero_pad) {
|
|
if (1 != writefunc(&sign_char, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
width--;
|
|
} else {
|
|
*--numptr = sign_char;
|
|
len = 0;
|
|
}
|
|
}
|
|
if (left_justify) {
|
|
n = writefunc(numptr, len, arg1, arg2);
|
|
total += n;
|
|
if (n != len)
|
|
break;
|
|
}
|
|
while (len < width) {
|
|
if (1 != writefunc(padstr, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
width--;
|
|
}
|
|
if (!left_justify)
|
|
total += writefunc(numptr, len, arg1, arg2);
|
|
break;
|
|
} /* case 'd', 'i' */
|
|
|
|
case 'c': {
|
|
const char *padstr = zero_pad ? "0" : " ";
|
|
unsigned char c = (unsigned char) intval;
|
|
|
|
if (left_justify) {
|
|
if (1 != writefunc((char *)&c, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
}
|
|
while (width > 1) {
|
|
if (1 != writefunc(padstr, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
width--;
|
|
}
|
|
if (!left_justify)
|
|
total += writefunc((char *)&c, 1, arg1, arg2);
|
|
break;
|
|
} /* case 'c' */
|
|
|
|
case 's': {
|
|
const char *padstr;
|
|
int len;
|
|
|
|
/* Catch null strings */
|
|
if (strval == NULL) {
|
|
strval = "(null)";
|
|
}
|
|
handle_string: /* NULL pointers for %p come here */
|
|
padstr = zero_pad ? "0" : " ";
|
|
len = strlen(strval);
|
|
if (precision < 0 || precision > len)
|
|
precision = len;
|
|
if (left_justify && precision > 0) {
|
|
n = writefunc(strval, precision, arg1, arg2);
|
|
total += n;
|
|
if (n != precision)
|
|
break;
|
|
}
|
|
while (width > precision) {
|
|
if (1 != writefunc(padstr, 1, arg1, arg2))
|
|
break;
|
|
total++;
|
|
width--;
|
|
}
|
|
if (!left_justify && precision > 0)
|
|
total += writefunc(strval, precision, arg1, arg2);
|
|
break;
|
|
} /* case 's' */
|
|
|
|
} /* switch (*format++) */
|
|
|
|
startptr = format; /* Start again after this %-token */
|
|
} /* while (*format) */
|
|
|
|
/* Write anything left over. */
|
|
if (startptr != format)
|
|
total += writefunc(startptr, format-startptr, arg1, arg2);
|
|
|
|
/* Return total bytes written. */
|
|
return total;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int writefunc(const char *buf, size_t len, char **string, size_t *size)
|
|
{
|
|
if ((*size) <= 0)
|
|
return 0;
|
|
if (len > (*size)-1)
|
|
len = (*size)-1;
|
|
if (len > 0)
|
|
memcpy((*string), buf, len);
|
|
(*string) += len;
|
|
(*string)[0] = 0;
|
|
(*size) -= len;
|
|
return len;
|
|
}
|
|
|
|
int my_vsnprintf(char *string, size_t size, const char *format, va_list args)
|
|
{
|
|
int ret;
|
|
|
|
if (size <= 0)
|
|
return 0;
|
|
ret = _pfmt(format, args, (_pfmt_writefunc_t) writefunc, &string, &size);
|
|
return ret;
|
|
}
|
|
|
|
int my_snprintf(char *string, size_t size, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
int ret;
|
|
|
|
va_start(args, format);
|
|
ret = my_vsnprintf(string, size, format, args);
|
|
va_end(args);
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-file-style: "stroustrup"
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vim: expandtab shiftwidth=4:
|
|
*/
|