#include "sys_defs.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <float.h>
#include <errno.h>
#include <limits.h>
#include "msg.h"
#include "vbuf.h"
#include "vstring.h"
#include "vbuf_print.h"
#define INT_SPACE ((CHAR_BIT * sizeof(long)) / 2)
#define DBL_SPACE ((CHAR_BIT * sizeof(double)) / 2 + DBL_MAX_10_EXP)
#define PTR_SPACE ((CHAR_BIT * sizeof(char *)) / 2)
#ifndef NO_SNPRINTF
#define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \
ssize_t _ret; \
VBUF_SPACE((bp), (sz)); \
_ret = snprintf((char *) (bp)->ptr, (bp)->cnt, (fmt), (arg)); \
if (_ret < 0) \
msg_panic("%s: output error for '%s'", myname, (fmt)); \
if (_ret >= (bp)->cnt) \
msg_panic("%s: output for '%s' exceeds space %ld", \
myname, fmt, (long) (bp)->cnt); \
VBUF_SKIP(bp); \
} while (0)
#else
#define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \
VBUF_SPACE((bp), (sz)); \
sprintf((char *) (bp)->ptr, (fmt), (arg)); \
VBUF_SKIP(bp); \
} while (0)
#endif
#define VBUF_SKIP(bp) do { \
while ((bp)->cnt > 0 && *(bp)->ptr) \
(bp)->ptr++, (bp)->cnt--; \
} while (0)
#define VSTRING_ADDNUM(vp, n) do { \
VBUF_SNPRINTF(&(vp)->vbuf, INT_SPACE, "%d", n); \
} while (0)
#define VBUF_STRCAT(bp, s) do { \
unsigned char *_cp = (unsigned char *) (s); \
int _ch; \
while ((_ch = *_cp++) != 0) \
VBUF_PUT((bp), _ch); \
} while (0)
VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
{
const char *myname = "vbuf_print";
static VSTRING *fmt;
unsigned char *cp;
int width;
int prec;
unsigned long_flag;
int ch;
char *s;
int saved_errno = errno;
if (fmt == 0)
fmt = vstring_alloc(INT_SPACE);
for (cp = (unsigned char *) format; *cp; cp++) {
if (*cp != '%') {
VBUF_PUT(bp, *cp);
} else if (cp[1] == '%') {
VBUF_PUT(bp, *cp++);
} else {
VSTRING_RESET(fmt);
VSTRING_ADDCH(fmt, *cp++);
if (*cp == '-')
VSTRING_ADDCH(fmt, *cp++);
if (*cp == '+')
VSTRING_ADDCH(fmt, *cp++);
if (*cp == '0')
VSTRING_ADDCH(fmt, *cp++);
if (*cp == '*') {
width = va_arg(ap, int);
VSTRING_ADDNUM(fmt, width);
cp++;
} else {
for (width = 0; ch = *cp, ISDIGIT(ch); cp++) {
int digit = ch - '0';
if (width > INT_MAX / 10
|| (width *= 10) > INT_MAX - digit)
msg_panic("%s: bad width %d... in %.50s",
myname, width, format);
width += digit;
VSTRING_ADDCH(fmt, ch);
}
}
if (*cp == '.') {
VSTRING_ADDCH(fmt, *cp++);
if (*cp == '*') {
prec = va_arg(ap, int);
VSTRING_ADDNUM(fmt, prec);
cp++;
} else {
for (prec = 0; ch = *cp, ISDIGIT(ch); cp++) {
int digit = ch - '0';
if (prec > INT_MAX / 10
|| (prec *= 10) > INT_MAX - digit)
msg_panic("%s: bad precision %d... in %.50s",
myname, prec, format);
prec += digit;
VSTRING_ADDCH(fmt, ch);
}
}
} else {
prec = -1;
}
if ((long_flag = (*cp == 'l')) != 0)
VSTRING_ADDCH(fmt, *cp++);
if (*cp == 0)
break;
VSTRING_ADDCH(fmt, *cp);
VSTRING_TERMINATE(fmt);
switch (*cp) {
case 's':
if (long_flag)
msg_panic("%s: %%l%c is not supported", myname, *cp);
s = va_arg(ap, char *);
if (prec >= 0 || (width > 0 && width > strlen(s))) {
VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE,
vstring_str(fmt), s);
} else {
VBUF_STRCAT(bp, s);
}
break;
case 'c':
if (long_flag)
msg_panic("%s: %%l%c is not supported", myname, *cp);
case 'd':
case 'u':
case 'o':
case 'x':
case 'X':
if (long_flag)
VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE,
vstring_str(fmt), va_arg(ap, long));
else
VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE,
vstring_str(fmt), va_arg(ap, int));
break;
case 'e':
case 'f':
case 'g':
VBUF_SNPRINTF(bp, (width > prec ? width : prec) + DBL_SPACE,
vstring_str(fmt), va_arg(ap, double));
break;
case 'm':
VBUF_STRCAT(bp, strerror(saved_errno));
break;
case 'p':
if (long_flag)
msg_panic("%s: %%l%c is not supported", myname, *cp);
VBUF_SNPRINTF(bp, (width > prec ? width : prec) + PTR_SPACE,
vstring_str(fmt), va_arg(ap, char *));
break;
default:
msg_panic("vbuf_print: unknown format type: %c", *cp);
break;
}
}
}
return (bp);
}
#ifdef TEST
#include <argv.h>
#include <msg_vstream.h>
#include <vstring.h>
#include <vstring_vstream.h>
int main(int argc, char **argv)
{
VSTRING *ibuf = vstring_alloc(100);
msg_vstream_init(argv[0], VSTREAM_ERR);
while (vstring_fgets_nonl(ibuf, VSTREAM_IN)) {
ARGV *args = argv_split(vstring_str(ibuf), CHARS_SPACE);
char *cp;
if (args->argc == 0 || *(cp = args->argv[0]) == '#') {
;
} else if (args->argc != 2 || *cp != '%') {
msg_warn("usage: format number");
} else {
char *fmt = cp++;
int lflag;
cp += strspn(cp, "+-*0123456789.");
if ((lflag = (*cp == 'l')) != 0)
cp++;
if (cp[1] != 0) {
msg_warn("bad format: \"%s\"", fmt);
} else {
VSTRING *obuf = vstring_alloc(1);
char *val = args->argv[1];
#ifdef CA_VSTRING_CTL_EXACT
vstring_ctl(obuf, CA_VSTRING_CTL_EXACT, CA_VSTRING_CTL_END);
#endif
switch (*cp) {
case 'c':
case 'd':
case 'o':
case 'u':
case 'x':
case 'X':
if (lflag)
vstring_sprintf(obuf, fmt, atol(val));
else
vstring_sprintf(obuf, fmt, atoi(val));
msg_info("\"%s\"", vstring_str(obuf));
break;
case 's':
vstring_sprintf(obuf, fmt, val);
msg_info("\"%s\"", vstring_str(obuf));
break;
case 'f':
case 'g':
vstring_sprintf(obuf, fmt, atof(val));
msg_info("\"%s\"", vstring_str(obuf));
break;
default:
msg_warn("bad format: \"%s\"", fmt);
break;
}
vstring_free(obuf);
}
}
argv_free(args);
}
vstring_free(ibuf);
return (0);
}
#endif