static size_t itoa(unsigned num, char *dst, unsigned base, int issigned, int addplus, int uppercase) {
int a = uppercase ? 'A' : 'a';
int negative = 0;
char *d = dst;
size_t len;
if (num == 0) {
*d = '0';
return 1;
}
if (issigned && (int)num < 0 && base == 10) {
negative = 1;
num = -num;
}
while (num > 0) {
unsigned rem = num % base;
num /= base;
*d++ = (rem > 9) ? (rem - 10 + a) : (rem + '0');
}
if (negative)
*d++ = '-';
else if (addplus)
*d++ = '+';
len = d - dst;
reverse(dst, len);
return len;
}
static size_t lltoa(unsigned long long num, char *dst, unsigned base, int issigned, int addplus, int uppercase) {
int a = uppercase ? 'A' : 'a';
int negative = 0;
char *d = dst;
size_t len;
if (num == 0) {
*d = '0';
return 1;
}
if (issigned && (signed long long)num < 0 && base == 10) {
negative = 1;
num = -num;
}
while (num > 0) {
unsigned rem = num % base;
num /= base;
*d++ = (rem > 9) ? (rem - 10 + a) : (rem + '0');
}
if (negative)
*d++ = '-';
else if (addplus)
*d++ = '+';
if ((ch = *fmt++) == '\0')
goto out;
} else if (ch == 'l') {
if ((ch = *fmt++) == '\0')
goto out;
if (ch == 'l') {
longlong = 1;
if ((ch = *fmt++) == '\0')
goto out;
}
} else if (ch == 'h') {
half = 1;
if ((ch = *fmt++) == '\0')
goto out;
}
switch (ch) {
default:
fmt = start;
/* Fall through */
case '%':
PUTCH('%');
break;
case 'C':
case 'c':
PUTCH(va_arg(ap, wchar_t));
break;
case 'D':
case 'd':
uppercase = (ch == 'D');
if (longlong)
len = lltoa(va_arg(ap, long long), tmp, 10, 1, addplus, uppercase);
else
len = itoa(va_arg(ap, int), tmp, 10, 1, addplus, uppercase);
hsrc = tmp;
if (width > len)
width -= len;
else
width = 0;
if (left == 0) {
for (i = 0; i < width; i++) PUTCH(padzeros ? '0' : ' ');
}
for (i = 0; i < len; i++) PUTCH(hsrc[i]);
if (left != 0) {
for (i = 0; i < width; i++) PUTCH(' ');
}
break;
case 'U':
case 'u':
uppercase = (ch == 'U');
if (longlong)
len = lltoa(va_arg(ap, unsigned long long), tmp, 10, 0, addplus, uppercase);
else
len = itoa(va_arg(ap, unsigned int), tmp, 10, 0, addplus, uppercase);
hsrc = tmp;
if (width > len)
width -= len;
else
width = 0;
if (left == 0) {
for (i = 0; i < width; i++) PUTCH(padzeros ? '0' : ' ');
}
for (i = 0; i < len; i++) PUTCH(hsrc[i]);
if (left != 0) {
for (i = 0; i < width; i++) PUTCH(' ');
}
break;
case 'X':
case 'x':
uppercase = (ch == 'X');
if (longlong)
len = lltoa(va_arg(ap, unsigned long long), tmp, 16, 0, addplus, uppercase);
else
len = itoa(va_arg(ap, unsigned int), tmp, 16, 0, addplus, uppercase);
hsrc = tmp;
if (width > len)
width -= len;
else
width = 0;
if (left == 0) {
for (i = 0; i < width; i++) PUTCH(padzeros ? '0' : ' ');
}
for (i = 0; i < len; i++) PUTCH(hsrc[i]);
if (left != 0) {
for (i = 0; i < width; i++) PUTCH(' ');
}
break;
case 'P':
case 'p':
uppercase = (ch == 'P');
ptr = va_arg(ap, void *);
len = itoa((unsigned)ptr, tmp, 16, 0, 0, uppercase);
hsrc = tmp;
width = 8;
if (width > len)
width -= len;
else
width = 0;
if (alternate && ptr != NULL) {
PUTCH('0');
PUTCH('x');
}
for (i = 0; i < width; i++) PUTCH('0');
for (i = 0; i < len; i++) PUTCH(hsrc[i]);
break;
case 'S':
case 's':
if (half) {
hsrc = va_arg(ap, const char *);
if (hsrc == NULL)
hsrc = "(null)";
len = strlen(hsrc);
if (limit > 0 && len > limit)
len = limit;
if (width > len)
width -= len;
else
width = 0;
if (left == 0) {
for (i = 0; i < width; i++) PUTCH(' ');
}
for (i = 0; i < len; i++) PUTCH(hsrc[i]);
if (left != 0) {
for (i = 0; i < width; i++) PUTCH(' ');
}
} else {
src = va_arg(ap, const wchar_t *);
if (src == NULL)
src = L"(null)";
len = wcslen(src);
if (limit > 0 && len > limit)
len = limit;
if (width > len)
width -= len;
else
width = 0;
if (left == 0) {
for (i = 0; i < width; i++) PUTCH(' ');
}
for (i = 0; i < len; i++) PUTCH(src[i]);
if (left != 0) {
for (i = 0; i < width; i++) PUTCH(' ');
}
}
break;
}
} else {
PUTCH(ch);
}
}
out:
PUTCH('\0');
return pcd.count;
}
int swprintf(wchar_t *buffer, size_t maxlen, const wchar_t *fmt, ...) {
va_list ap;
int wc;
#define ZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
#define LEFT 16 /* left justified */
#define SPECIAL 32 /* 0x */
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
#define do_div(n,base) ({ \
int __res; \
__res = ((unsigned long long) n) % (unsigned) base; \
n = ((unsigned long long) n) / (unsigned) base; \
__res; })
static int skip_atoi(const wchar_t **s)
{
int i=0;
while (iswdigit(**s))
i = i*10 + *((*s)++) - L'0';
return i;
}
static wchar_t *
number (wchar_t *str, long long num, int base, int size, int precision,
int type)
{
wchar_t c,sign,tmp[66];
const wchar_t *digits = L"0123456789abcdefghijklmnopqrstuvwxyz";
int i;
if (type & LARGE)
digits = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (type & LEFT)
type &= ~ZEROPAD;
if (base < 2 || base > 36)
return 0;
c = (type & ZEROPAD) ? L'0' : L' ';
sign = 0;
if (type & SIGN)
{
if (num < 0)
{
sign = L'-';
num = -num;
size--;
}
else if (type & PLUS)
{
sign = L'+';
size--;
}
else if (type & SPACE)
{
sign = L' ';
size--;
}
}
if (type & SPECIAL)
{
if (base == 16)
size -= 2;
else if (base == 8)
size--;
}
i = 0;
if (num == 0)
tmp[i++]='0';
else while (num != 0)
tmp[i++] = digits[do_div(num,base)];
if (i > precision)
precision = i;
size -= precision;
if (!(type&(ZEROPAD+LEFT)))
while(size-->0)
*str++ = L' ';
if (sign)
*str++ = sign;
if (type & SPECIAL)
{
if (base==8)
{
*str++ = L'0';
}
else if (base==16)
{
*str++ = L'0';
*str++ = digits[33];
}
}
if (!(type & LEFT))
while (size-- > 0)
*str++ = c;
while (i < precision--)
*str++ = '0';
while (i-- > 0)
*str++ = tmp[i];
while (size-- > 0)
*str++ = L' ';
return str;
}
int _vsnwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, va_list args)
{
int len;
unsigned long long num;
int i, base;
wchar_t * str;
const char *s;
const wchar_t *sw;
int flags; /* flags to number() */
int field_width; /* width of output field */
int precision; /* min. # of digits for integers; max
number of chars for from string */
int qualifier; /* 'h', 'l', 'L', 'w' or 'I' for integer fields */
for (str=buf ; *fmt ; ++fmt) {
if (*fmt != L'%') {
*str++ = *fmt;
continue;
}
/* process flags */
flags = 0;
repeat:
++fmt; /* this also skips first '%' */
switch (*fmt) {
case L'-': flags |= LEFT; goto repeat;
case L'+': flags |= PLUS; goto repeat;
case L' ': flags |= SPACE; goto repeat;
case L'#': flags |= SPECIAL; goto repeat;
case L'0': flags |= ZEROPAD; goto repeat;
}
/* get field width */
field_width = -1;
if (iswdigit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == L'*') {
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == L'.') {
++fmt;
if (iswdigit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == L'*') {
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
switch (*fmt) {
case L'c':
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = L' ';
if (qualifier == 'h')
*str++ = (wchar_t) va_arg(args, int);
else
*str++ = (wchar_t) va_arg(args, int);
while (--field_width > 0)
*str++ = L' ';
continue;
case L'C':
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = L' ';
if (qualifier == 'l' || qualifier == 'w')
*str++ = (wchar_t) va_arg(args, int);
else
*str++ = (wchar_t) va_arg(args, int);
while (--field_width > 0)
*str++ = L' ';
continue;
case L's':
if (qualifier == 'h') {
/* print ascii string */
s = va_arg(args, char *);
if (s == NULL)
s = "<NULL>";
len = strlen (s);
if ((unsigned int)len > (unsigned int)precision)
len = precision;
if (!(flags & LEFT))
while (len < field_width--)
*str++ = L' ';
for (i = 0; i < len; ++i)
*str++ = (wchar_t)(*s++);
while (len < field_width--)
*str++ = L' ';
} else {
/* print unicode string */
sw = va_arg(args, wchar_t *);
if (sw == NULL)
sw = L"<NULL>";
len = wcslen (sw);
if ((unsigned int)len > (unsigned int)precision)
len = precision;
if (!(flags & LEFT))
while (len < field_width--)
*str++ = L' ';
for (i = 0; i < len; ++i)
*str++ = *sw++;
while (len < field_width--)
*str++ = L' ';
}
continue;
case L'S':
if (qualifier == 'l' || qualifier == 'w') {
/* print unicode string */
sw = va_arg(args, wchar_t *);
if (sw == NULL)
sw = L"<NULL>";
len = wcslen (sw);
if ((unsigned int)len > (unsigned int)precision)
len = precision;
if (!(flags & LEFT))
while (len < field_width--)
*str++ = L' ';
for (i = 0; i < len; ++i)
*str++ = *sw++;
while (len < field_width--)
*str++ = L' ';
} else {
/* print ascii string */
s = va_arg(args, char *);
if (s == NULL)
s = "<NULL>";
len = strlen (s);
if ((unsigned int)len > (unsigned int)precision)
len = precision;
if (!(flags & LEFT))
while (len < field_width--)
*str++ = L' ';
for (i = 0; i < len; ++i)
*str++ = (wchar_t)(*s++);
while (len < field_width--)
*str++ = L' ';
}
continue;
case L'p':
if (field_width == -1) {
field_width = 2*sizeof(void *);
flags |= ZEROPAD;
}
str = number(str,
(unsigned long) va_arg(args, void *), 16,
field_width, precision, flags);
continue;
case L'n':
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
*ip = (str - buf);
} else {
int * ip = va_arg(args, int *);
*ip = (str - buf);
}
continue;
/* integer number formats - set up the flags and "break" */
case L'o':
base = 8;
break;
case L'b':
base = 2;
break;
case L'X':
flags |= LARGE;
case L'x':
base = 16;
break;
case L'd':
case L'i':
flags |= SIGN;
case L'u':
break;
default:
if (*fmt != L'%')
*str++ = L'%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
continue;
}
if (qualifier == 'I')
num = va_arg(args, unsigned long long);
else if (qualifier == 'l')
num = va_arg(args, unsigned long);
else if (qualifier == 'h') {
if (flags & SIGN)
num = va_arg(args, int);
else
num = va_arg(args, unsigned int);
}
else {
if (flags & SIGN)
num = va_arg(args, int);
else
num = va_arg(args, unsigned int);
}
str = number(str, num, base, field_width, precision, flags);
}
*str = L'\0';
return str-buf;
}
int swprintf(wchar_t *buf, size_t n,const wchar_t *fmt, ...)
{
va_list args;
int i;
int vswprintf(wchar_t *buf, size_t n, const wchar_t *fmt, va_list args)
{
return _vsnwprintf(buf,n,fmt,args);
}
Both of them working, but both of them didn't handle floating point. I.e. there in both realisations no case 'f': code, and because of that , when i want to do something like this:
@flash It should be done inside of swprintf()/vsnwprintf() for making porting in future easer, and without needs to touch code which use them. That swprintf() offten need it, and will be good to deal with floating point support in it once and for all.
Thanks a bunch! Checked library : with gcc it works, with g++ not, saing about undefs. I assume there need some C++ guards as usually inside of libwprinf.h ? Maybe forgotten extern "C" or something ?
Thanks a bunch! Checked library : with gcc it works, with g++ not, saing about undefs. I assume there need some C++ guards as usually inside of libwprinf.h ? Maybe forgotten extern "C" or something ?
The NUL character is needed for the correct working of the swprintf/vswprintf functions but should not be output for any of the other wprintf functions (as you point out).
I've moved the code for outputting the final NUL character to vswprintf where it doesn't interfere any more. This change also made the implementation of the PUTWC() macro simpler.
@all While it surely not related to salas00 implementation of swprintf, find out today that I have bugs with some code I tried to port, which have such stuff:
Where "i" is 0,1 or 2 (and it have it for sure, I checked by printf). But then, such a swprintf draw on the screen not "Slot 1" or "Slot 2", but instead "Slot %i". Like it didn't take into account. And I can't be sure, but probably that causes sometimes crashes as well.
Is it just bad code of swprintf there? Code done on windows, so everything possible.
EDIT: oh, replaced %i on %d and it works!
@frederik Didn't we have %i and %I implemented there? And if not, can it be that cause of crashes, or it surely not related ?