Code:
#include <stdio.h>
#include <string.h>
int qualifier(char c) {
static char lookup[] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1
};
return(c >= 'E' && c <= 's'&& lookup[c - 'E'] == 1);
}
#define LOWER(C) (((C) >= 'A' && (C) <= 'Z')? (C) - 'A' + 'a' : (C))
int xsprintf(char *dst, const char *fmt, void **args) {
char *end = dst;
const char *fmt_begin;
void *arg;
if(!dst || !fmt || !args)
return(-1);
while(*fmt)
{
if(*fmt == '%')
{
fmt_begin = fmt++;
if(*fmt == '%')
*end++ = '%';
else
{
char fmt2[8] = { '%' };
int len;
for(; !qualifier(*fmt); ++fmt);
len = fmt - fmt_begin;
if(len)
memcpy(fmt2 + 1, fmt_begin + 1, len);
fmt2[len] = *fmt, fmt2[len + 1] = 0;
arg = *args++;
switch(*fmt)
{
case 'c':
*end++ = *(char*)arg;
break;
case 'x':
case 'X':
case 'o':
case 'i':
case 'd':
{
char buffer[sizeof(long) * 8];
unsigned long val;
val = *(fmt - 1) == 'h'? *(short*)arg :
*(fmt - 1) == 'd'? *(int*)arg :
*fmt == 'p'? (unsigned long)arg : *(long*)arg;
len = sprintf(buffer, fmt2, val);
if(len < 0)
return(-1);
memcpy(end, buffer, len);
end += len;
}
break;
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
{
char buffer[512];
long double val = LOWER(*(fmt - 1)) == 'h'?
*(float*)arg : LOWER(*(fmt - 1)) == 'L'?
*(long double*)arg : *(double*)arg;
fmt2[len] = 'L', fmt2[len + 1] = 'f', fmt2[len + 2] = 0;
len = sprintf(buffer, fmt2, val);
if(len < 0)
return(-1);
memcpy(end, buffer, len);
end += len;
}
break;
case 's':
{
if(*fmt_begin == '.')
{
len = (int)strtol(fmt_begin, NULL, 10);
strncpy(end, (const char*)arg, len);
}
else
{
len = (int)strlen((const char*)arg);
memcpy(end, (const char*)arg, len);
}
end += len;
}
break;
case 'n':
*(int*)arg = end - dst;
break;
default:
return(-1);
}
}
}
else
*end++ = *fmt;
++fmt;
}
*end = 0;
return(end - dst);
}
int main()
{
char s[100];
int i = 10;
double d = 0.5;
char c = 'y';
void *p[] = { &i, &d, &c, "Foo" };
xsprintf(s, "%3d %.2f %c %s %%100", p);
puts(s);
}
There are several problems with my function: There's no format string checking (You could achieve this via compiler builtins, like the ones included in gcc), there's no support for some of the newest additions ($ position modifiers; 'z', 'j' and 't' qualifiers; and lack of locale-dependant grouping) and there may be an overflow with very large floating point values (Don't know if 512 characters is enough).