sima/autoconf/
sima/hosts/i386/
sima/mudlib/
sima/mudlib/kernel/
sima/mudlib/obj/
sima/mudlib/sys/
sima/synhash/mips/
/* 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;
}