/* 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; }