/* Copyright 1997 J"orn Rennecke */
#include <stdio.h>
#include "common.h"
static struct {
p_int width, prec, *prec_defaultp;
char pad, sign;
p_int dummy;
} modifier;
static uint8 *get_counts(uint8 *fmt, uint8* fmt_end, svalue **argp) {
int sign = 0;
p_int count = 0, *countp = &modifier.width;
modifier.pad = ' ';
modifier.sign = '0';
modifier.prec = 0;
modifier.prec_defaultp = &modifier.prec;
for (;fmt < fmt_end; fmt++) {
switch (*fmt) {
case '+':
modifier.sign = '+';
case ' ':
modifier.sign = ' ';
case '-':
sign = 1;
break;
case '0':
if (! count)
modifier.pad = '0';
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
count = count * 10 + *fmt - '0';
break;
case '*':
{
svalue sv = *(*argp)++;
if (! SV_IS_NUMBER (sv))
bad_efun_arg(0);
count = sv.i >> 1;
break;
}
case '.':
if (sign)
count = -count;
sign = 0;
modifier.width = count;
countp = &modifier.prec;
modifier.prec_defaultp = &modifier.dummy;
count = 0;
break;
default:
if (sign)
count = -count;
*countp = count;
return fmt;
}
}
return fmt;
}
uint8 *add_padded(uint8 *dest, uint8 *src, p_int len) {
if (modifier.width < 0 && len < -modifier.width) {
memset(dest, modifier.pad, modifier.width - len);
dest += modifier.width - len;
memcpy(dest, src, len);
dest += len;
} else if (len < modifier.width) {
memset(dest, modifier.pad, modifier.width - len);
dest += modifier.width - len;
memcpy(dest, src, len);
dest += len;
} else {
memcpy(dest, src, len);
dest += len;
}
return dest;
}
svalue *f_sprintf(svalue *sp, int total_arg) {
mp_int total_len = 0;
svalue fmt_sv = sp[-total_arg + 1];
svalue *argp = &sp[-total_arg + 2];
struct counted_string fmt_cs;
uint8 *fmt, *fmt_end, *dest;
svalue res;
if (!SV_IS_STRING(fmt_sv)) {
bad_efun_arg(1);
return sp;
}
fmt_cs = sv_string2(fmt_sv);
fmt_end = fmt_cs.start + fmt_cs.len;
/* Determine the length of the result. */
for (fmt = fmt_cs.start; fmt < fmt_end;) {
if (*fmt++ == '%' && fmt < fmt_end) {
uint8 c;
static char fmt1[] = "%+X";
char buf[P_INT_PRINT_SIZE];
int len;
fmt = get_counts(fmt, fmt_end, &argp);
if (fmt >= fmt_end)
break;
switch (c = *fmt++) {
default:
break;
case 'd': case 'x': case 'X':
fmt1[1] = modifier.sign;
fmt1[2] = c;
sprintf(buf, fmt1, argp++->i>>1);
if (modifier.width < 0)
modifier.width = -modifier.width;
len = strlen(buf);
total_len += modifier.width > len ? modifier.width : len;
break;
case 's':
{
svalue sv = *argp++;
if (!SV_IS_STRING(sv)) {
} else {
*modifier.prec_defaultp = P_INT_MAX;
len = SV_ANYSTRLEN(sv);
if (len > modifier.prec)
len = modifier.prec;
if (len < modifier.width)
len = modifier.width;
total_len += len;
}
}
break;
}
} else
total_len++;
}
if (argp != sp + 1)
bad_efun_arg(1);
if (inter_errno)
return sp;
/* Allocate the memory for the result. */
if (total_len > MAX_SMALL_STRING) {
res = ALLOC_LSTRING(total_len);
if (!res.p)
goto out_of_memory;
dest = SV_LSTRING(res);
SV_LSTRREF(res) = 0;
SV_LSTRLEN(res) = total_len;
} else {
res = ALLOC_STRING(total_len);
if (!res.p) {
out_of_memory:
error(IE_NOMEM);
return sp;
}
dest = SV_STRING(res);
SV_STRREF(res) = 0;
SV_STRLEN(res) = total_len;
}
/* Now do the actual conversions. */
argp = &sp[-total_arg + 2];
for (fmt = fmt_cs.start; fmt < fmt_end;) {
uint8 c = *fmt++;
if (c == '%' && fmt < fmt_end) {
static char fmt1[] = "%+X";
char buf[11];
fmt = get_counts(fmt, fmt_end, &argp);
if (fmt >= fmt_end)
break;
switch (c = *fmt++) {
default:
break;
case 'd': case 'x': case 'X':
fmt1[1] = modifier.sign;
fmt1[2] = c;
sprintf(buf, fmt1, argp++->i>>1);
dest = add_padded(dest, buf, strlen(buf));
break;
case 's':
{
svalue sv = *argp++;
if (!SV_IS_STRING(sv)) {
} else {
struct counted_string str;
*modifier.prec_defaultp = P_INT_MAX;
str = sv_string2(sv);
if (str.len > modifier.prec)
str.len = modifier.prec;
dest = add_padded(dest, str.start, str.len);
}
break;
}
}
} else
*dest++ = c;
}
do {
svalue sv = *sp--;
FREE_SVALUE(sv);
} while (--total_arg);
*++sp = res;
return sp;
}