/*
* LIB/SNPRINTF.C - snprintf module if we don't have it
*
* snprintf() is just too important, we MUST have it, so if the OS
* doesn't support this module will include it. Unfortunately, to
* do it right also required me to write an equivalent pfmt(), so
* all the snprintf()'s in the code support only a small subset
* of valid '%' escapes:
*
* %[-][0][n][l]d
* %[-][0][n][l]u
* %[-][0][n][l]x
* %[-][0][n]c
* %[-][n]s
*
* NOTE!!!!! this snprintf only supports %x, %d, %c, and %s
*
* (c)Copyright 1998, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*/
#include "defs.h"
int MiniPfmt(const char *ctl, va_list va, void (*callback)(void *info, const char *buf, int bytes), void *info);
#ifdef TEST
#undef HAVE_SNPRINTF
#define HAVE_SNPRINTF 0
void fatal(const char *ctl, ...);
#endif
#if USE_INTERNAL_SNPRINTF
#undef HAVE_SNPRINTF
#define HAVE_SNPRINTF 0
#endif
#if HAVE_SNPRINTF == 0
/*
* no prototypes, snprintf is extern'd in lib/defs.h since it may
* be conditionally compiled.
*/
int
snprintf(char *str, size_t size, const char *ctl, ...)
{
va_list va;
int r;
va_start(va, ctl);
r = vsnprintf(str, size, ctl, va);
va_end(va);
return(r);
}
typedef struct SBuf {
char *sb_Buf;
int sb_Len;
} SBuf;
void
vsncallback(void *info, const char *buf, int bytes)
{
SBuf *sbuf = info;
if (bytes > sbuf->sb_Len)
bytes = sbuf->sb_Len;
if (bytes) {
memcpy(sbuf->sb_Buf, buf, bytes);
sbuf->sb_Buf += bytes;
sbuf->sb_Len -= bytes;
}
}
int
vsnprintf(char *str, size_t size, const char *ctl, va_list va)
{
SBuf sbuf;
int r;
sbuf.sb_Buf = str;
sbuf.sb_Len = size - 1;
r = MiniPfmt(ctl, va, vsncallback, &sbuf);
*sbuf.sb_Buf = 0;
if (r > size)
r = size;
return(r);
}
#define FMT_LONG 0x01
#define FMT_LEFT 0x02
#define FMT_ZFILL 0x04
#define FMT_RIGHT 0x08
#define FMT_ISNEG 0x10
int
MiniPfmt(const char *ctl, va_list va, void (*callback)(void *info, const char *buf, int bytes), void *info)
{
int r;
int i;
int b;
r = 0;
for (i = b = 0; ctl[i]; ++i) {
int flags;
int fwidth;
char tbuf[32];
char *tptr;
int tlen;
if (ctl[i] != '%')
continue;
if (i != b) {
callback(info, ctl + b, i - b);
r += i - b;
}
flags = FMT_RIGHT;
fwidth = 0; /* exact fit */
/*
* pointer to temporary buffer (for numbers),
* one past so we can put a minus sign before
* the beginning later on.
*/
tptr = tbuf + 1;
tlen = 0;
for (++i; ;++i) {
char c = ctl[i];
if (c == 0) {
fatal("MiniPfmt: unterminated % escape in %s", ctl);
/* not reached */
}
if (c == 's') { /* string */
tptr = va_arg(va, char *);
tlen = strlen(tptr);
break;
}
if (c == 'd' || c == 'u') { /* decimal */
long v;
if (flags & FMT_LONG)
v = va_arg(va, long);
else
v = va_arg(va, int);
if (c == 'd' && v < 0) {
v = -v;
flags |= FMT_ISNEG;
}
sprintf(tptr, "%lu", (unsigned long)v);
tlen = strlen(tptr);
break;
}
if (c == 'x') { /* hex */
if (flags & FMT_LONG) {
long v = va_arg(va, long);
sprintf(tptr, "%lx", v);
} else {
int v = va_arg(va, int);
sprintf(tptr, "%x", v);
}
tlen = strlen(tptr);
break;
}
if (c == 'c') {
tptr[0] = va_arg(va, int);
tlen = 1;
break;
}
if (c == '%') {
/*
* %% = '%'. tptr is empty
*/
tptr[0] = '%';
tlen = 1;
break;
}
switch(c) {
case '-':
flags |= FMT_LEFT;
flags &= ~FMT_RIGHT;
break;
case 'l':
flags |= FMT_LONG;
break;
case '0':
flags |= FMT_ZFILL;
/* fall through */
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/*
* field width
*/
{
char *ptr;
fwidth = strtol(ctl + i, &ptr, 10);
i = ptr - ctl - 1;
}
break;
default:
fatal("MiniPfmt: unknown ctl character '%c' in %s", c, ctl);
/* NOT REACHED */
}
}
/*
* Handle negative sign. It must occur prior to
* any zero-fill, but be next to a right-justified
* number.
*/
if (flags & FMT_ISNEG) {
if ((flags & FMT_ZFILL) && tlen < fwidth) {
--fwidth;
callback(info, "-", 1);
++r;
} else {
*--tptr = '-';
++tlen;
}
}
/*
* handle (default) right justification
* handle zero-fill
* (neither is set if buffer is left justified)
*/
while (tlen < fwidth) {
int n = (fwidth - tlen > 16) ? 16 : fwidth - tlen;
if (flags & FMT_ZFILL) {
callback(info, "0000000000000000", n);
fwidth -= n;
r += n;
continue;
} else if (flags & FMT_RIGHT) {
callback(info, " ", n);
fwidth -= n;
r += n;
continue;
}
break;
}
/*
* output tptr
*/
if (tlen) {
callback(info, tptr, tlen);
r += tlen;
}
/*
* handle left justification
*/
while (tlen < fwidth && (flags & FMT_LEFT)) {
int n = (fwidth - tlen > 16) ? 16 : fwidth - tlen;
callback(info, " ", n);
fwidth -= n;
r += n;
}
/*
* i points to the control terminator, so allow the for() loop to
* increment it.
*/
b = i + 1;
}
if (i != b) {
callback(info, ctl + b, i - b);
r += i - b;
}
return(r);
}
#endif
/*
*
* TEST CODE
*
*/
#ifdef TEST
int
main(int ac, char **av)
{
char buf[32];
int r;
if (ac == 1)
exit(0);
r = snprintf(buf, sizeof(buf), av[1], 203, 11, -500, "charlie");
printf("(%d): %s\n", r, buf);
exit(0);
}
void
fatal(const char *ctl, ...)
{
va_list va;
va_start(va, ctl);
vfprintf(stderr, ctl, va);
va_end(va);
fprintf(stderr, "\n");
fflush(stderr);
exit(1);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1