/
driver3.2@242/autoconf/
driver3.2@242/doc/LPC/
driver3.2@242/hosts/
driver3.2@242/hosts/amiga/NetIncl/
driver3.2@242/hosts/amiga/NetIncl/netinet/
driver3.2@242/hosts/amiga/NetIncl/sys/
driver3.2@242/hosts/atari/
driver3.2@242/hosts/fcrypt/
driver3.2@242/mudlib/
driver3.2@242/mudlib/sys/
driver3.2@242/util/
driver3.2@242/util/indent/hosts/next/
driver3.2@242/util/make_docs/
/*
 * sprintf.c v1.05 for LPMud 3.0.52
 *
 * An implementation of (s)printf() for LPC, with quite a few
 * extensions (note that as no floating point exists, some parameters
 * have slightly different meaning or restrictions to "standard"
 * (s)printf.)  Implemented by Lynscar (Sean A Reith).
 *
 * This version supports the following as modifiers:
 *  " "   pad positive integers with a space.
 *  "+"   pad positive integers with a plus sign.
 *  "-"   left adjusted within field size.
 *        NB: std (s)printf() defaults to right justification, which is
 *            unnatural in the context of a mainly string based language
 *            but has been retained for "compatability" ;)
 *  "|"   centered within field size.
 *  "="   column mode if strings are greater than field size.  this is only
 *        meaningful with strings, all other types ignore
 *        this.  columns are auto-magically word wrapped.
 *  "#"   table mode, print a list of '\n' separated 'words' in a
 *        table within the field size.  only meaningful with strings.
 *   n    specifies the field size, a '*' specifies to use the corresponding
 *        arg as the field size.  if n is prepended with a zero, then is padded
 *        zeros, else it is padded with spaces (or specified pad string).
 *  "."n  presision of n, simple strings truncate after this (if presision is
 *        greater than field size, then field size = presision), tables use
 *        presision to specify the number of columns (if presision not specified
 *        then tables calculate a best fit), all other types ignore this.
 *  ":"n  n specifies the fs _and_ the presision, if n is prepended by a zero
 *        then it is padded with zeros instead of spaces.
 *  "@"   the argument is an array.  the corresponding format_info (minus the
 *        "@") is applyed to each element of the array.
 *  "'X'" The char(s) between the single-quotes are used to pad to field
 *        size (defaults to space) (if both a zero (in front of field
 *        size) and a pad string are specified, the one specified second
 *        overrules).  NOTE:  to include "'" in the pad string, you must
 *        use "\\'" (as the backslash has to be escaped past the
 *        interpreter), similarly, to include "\" requires "\\\\".
 * The following are the possible type specifiers.
 *  "%"   in which case no arguments are interpreted, and a "%" is inserted, and
 *        all modifiers are ignored.
 *  "O"   the argument is an LPC datatype.
 *  "s"   the argument is a string.
 *  "d"   the integer arg is printed in decimal.
 *  "i"   as d.
 *  "c"   the integer arg is to be printed as a character.
 *  "o"   the integer arg is printed in octal.
 *  "x"   the integer arg is printed in hex.
 *  "X"   the integer arg is printed in hex (in capitals).
 * e,E,f,F,g,G like in c.
 */

#include <stdio.h>
#include <setjmp.h>
#include <sys/types.h>

#include "config.h"
#include "lint.h"
#include "interpret.h"
#include "lang.h"
#include "instrs.h"
#include "exec.h"
#include "stdio.h"
#include "object.h"
#include "sent.h"

/*
 * If this #define is defined then error messages are returned,
 * otherwise error() is called (ie: A "wrongness in the fabric...")
 */
#define RETURN_ERROR_MESSAGES

#if defined(F_SPRINTF) || defined(F_PRINTF)

extern void free_svalue PROT((struct svalue *));

extern struct object *current_object;

typedef unsigned int format_info;
/*
 * Format of format_info:
 *   00000000 0000xxxx : argument type:
 *				0000 : type not found yet;
 *				0001 : error type not found;
 *				0010 : percent sign, null argument;
 *				0011 : LPC datatype;
 *				0100 : string;
 *				0101 : integer;
 *				0110 : float
 *   00000000 00xx0000 : justification:
 *				00 : right;
 *				01 : centre;
 *				10 : left;
 *   00000000 xx000000 : positive pad char:
 *				00 : none;
 *				01 : ' ';
 *				10 : '+';
 *   0000000x 00000000 : array mode?
 *   000000x0 00000000 : column mode?
 *   00000x00 00000000 : table mode?
 */

#define INFO_T 0xF
#define INFO_T_ERROR 0x1
#define INFO_T_NULL 0x2
#define INFO_T_LPC 0x3
#define INFO_T_STRING 0x4
#define INFO_T_INT 0x5
#define INFO_T_FLOAT 0x6

#define INFO_J 0x30
#define INFO_J_CENTRE 0x10
#define INFO_J_LEFT 0x20

#define INFO_PP 0xC0
#define INFO_PP_SPACE 0x40
#define INFO_PP_PLUS 0x80

#define INFO_ARRAY 0x100
#define INFO_COLS 0x200
#define INFO_TABLE 0x400

#define BUFF_SIZE 10000

#define ERROR(x) (longjmp(error_jmp, (x)))
#define ERR_ID_NUMBER	     0xFFFF
#define ERR_BUFF_OVERFLOW	0x1	/* buffer overflowed */
#define ERR_TO_FEW_ARGS		0x2	/* more arguments spec'ed than passed */
#define ERR_INVALID_STAR	0x3	/* invalid arg to * */
#define ERR_PRES_EXPECTED	0x4	/* expected presision not found */
#define ERR_INVALID_FORMAT_STR	0x5	/* error in format string */
#define ERR_INCORRECT_ARG	0x6	/* invalid arg to %[idcxXs] */
#define ERR_CST_REQUIRES_FS	0x7	/* field size not given for c/t */
#define ERR_UNDEFINED_TYPE	0x8	/* undefined type found */
#define ERR_QUOTE_EXPECTED	0x9	/* expected ' not found */
#define ERR_UNEXPECTED_EOS	0xA	/* fs terminated unexpectedly */
#define ERR_NULL_PS		0xB	/* pad string is null */
#define ERR_ARRAY_EXPECTED	0xC	/* array expected */

#define ERR_ARGUMENT	 0xFFFF0000

#define ERROR1(e,a) ERROR((e) | (a)<<16)
#define EXTRACT_ERR_ARGUMENT(i) ((i)>>16)

#define ADD_CHAR(x) {\
    buff[bpos++] = x;\
    if (bpos>BUFF_SIZE) ERROR(ERR_BUFF_OVERFLOW); \
}

#define ADD_STRN(s, n) { \
    if (bpos + n > BUFF_SIZE) ERROR(ERR_BUFF_OVERFLOW); \
    strncpy(buff+bpos, s, n); \
    bpos += n; \
}

#define ADD_CHARN(c, n) { \
    if (bpos + n > BUFF_SIZE) ERROR(ERR_BUFF_OVERFLOW); \
    memset(buff+bpos, c, n); \
    bpos += n; \
}

#define ADD_PADDING(pad, N) { \
    int n = (N); \
\
    if (!pad[1]) { \
	ADD_CHARN(*pad, n) \
    } else { \
	int l; \
\
	l = strlen(pad); \
	for (i=0; --n >= 0; ) { \
	    if (pad[i] == '\\') \
		i++; \
	    ADD_CHAR(pad[i]); \
	    if (++i == l) \
		i = 0; \
	} \
    } \
}

#define GET_NEXT_ARG {\
  if (++arg >= argc) ERROR(ERR_TO_FEW_ARGS); \
  carg = (argv+arg);\
}

#define SAVE_CHAR(pointer) {\
  savechars *new;\
  new = (savechars *)xalloc(sizeof(savechars));\
  new->what = *(pointer);\
  new->where = pointer;\
  new->next = saves;\
  saves = new;\
}

/*
 * list of characters to restore before exiting.
 */
typedef struct SaveChars {
  char what;
  char *where;
  struct SaveChars *next;
} savechars;

typedef struct ColumnSlashTable {
  union CSTData {
    char *col;			/* column data */
    char **tab;			/* table data */
  } d;				/* d == data */
  unsigned short int nocols;	/* number of columns in table *sigh* */
  char *pad;
  unsigned int start;		/* starting cursor position */
  unsigned int size;		/* column/table width */
  int pres;			/* presision */
  format_info info;		/* formatting data */
  struct ColumnSlashTable *next;
} cst;				/* Columns Slash Tables */

struct sprintf_buffer {
    /* char text[0]; */
#define BUF_TEXT(b) ((char *)(b))
    int offset;
    int size;
    char **start;
};

static char buff[BUFF_SIZE];	/* buffer for returned string */
unsigned int bpos;		/* position in buff */
unsigned int line_start;
jmp_buf error_jmp;		/* for error longjmp()s */

static struct sprintf_buffer *realloc_sprintf_buffer(b)
    struct sprintf_buffer *b;
{
    int offset   = b->offset;
    int size     = b->size;
    char **start = b->start;
    char *oldstart;

    oldstart = *start;
    do {
	if (size > BUFF_SIZE)
	    ERROR(ERR_BUFF_OVERFLOW);
	offset -= size;
	size <<= 1;
	*start = rexalloc(*start, size);
    } while (offset >= 0);
    b = (struct sprintf_buffer *)(*start+size - sizeof(struct sprintf_buffer));
    b->offset = offset;
    b->size = size;
    b->start = start;
    return b;
}

static void stradd(buffer, add)
    struct sprintf_buffer **buffer;
    char *add;
{
    register struct sprintf_buffer *b = *buffer;
    register int o;
    int len;

    len = strlen(add);
    o = b->offset;
    if ( (b->offset = o + len) >= 0) {
	*buffer = b = realloc_sprintf_buffer(b);
	o = b->offset - len;
    }
    strcpy(BUF_TEXT(b) + o, add);
}

static void numadd(buffer, num)
    struct sprintf_buffer **buffer;
    int num;
{
    struct sprintf_buffer *b = *buffer;
    int i,j,nve;

    if (num < 0) {
	if ( (num *= -1) < 0) {
	    numadd(buffer, num+1);
	    b = *buffer;
	    BUF_TEXT(b)[b->offset - 1] += 1;
	    return;
	}
	nve=1;
    } else nve=0;
    if (num >= 1000000000)
	j = nve + 10;
    else
	for (i=10, j=nve+1; num >= i; i*=10, j++) ;
    i = b->offset;
    if ((b->offset = i + j) >= 0) {
	*buffer = b = realloc_sprintf_buffer(b);
	i = b->offset - j;
    }
    BUF_TEXT(b)[i+j] = '\0';
    if (nve) {
	BUF_TEXT(b)[i] = '-';
	j--;
    } else
	i--;
    for (; j; j--, num /= 10)
	BUF_TEXT(b)[i+j] = (num%10) + '0';
} /* end of num_add() */

/*
 * This is a function purely because stradd() is, to keep same param
 * passing...
 */
void add_indent(buffer, indent)
    struct sprintf_buffer **buffer;
    int indent;
{
    int i;
    struct sprintf_buffer *b = *buffer;
    char *p;

    i = b->offset;
    if ( (b->offset = i + indent) >= 0) {
	*buffer = b = realloc_sprintf_buffer(b);
	i = b->offset - indent;
    }
    p = BUF_TEXT(b) + i;
    for (;indent;indent--)
	*p++ = ' ';
    *p = '\0';
}

static struct sprintf_buffer *svalue_to_string
  PROT((struct svalue *, struct sprintf_buffer *, int, int));

#ifdef MAPPINGS
struct stsf_locals {
    struct sprintf_buffer *spb;
    int indent;
    int num_values;
};

void svalue_to_string_filter(key, data, extra)
    struct svalue *key;
    struct svalue *data;
    char *extra;
{
    int i;
    struct stsf_locals *locals = (struct stsf_locals *)extra;
    char *delimiter = ":";

    i = locals->num_values;
    locals->spb =
      svalue_to_string(key, locals->spb, locals->indent, !i);
    while (--i >= 0) {
	stradd(&locals->spb, delimiter);
	locals->spb =
	  svalue_to_string(data++, locals->spb, 1, !i);
	delimiter = ";";
    }
}
#endif /* MAPPINGS */

/*
 * Converts any LPC datatype into an arbitrary string format
 * and returns a pointer to this string.
 * Scary number of parameters for a recursive function.
 */
static struct sprintf_buffer *svalue_to_string(obj, str, indent, trailing)
  struct svalue *obj;
  struct sprintf_buffer *str;
  int indent, trailing;
{
  int i;

  add_indent(&str, indent);
  switch (obj->type) {
    case T_INVALID:
      stradd(&str, "T_INVALID");
      break;
    case T_LVALUE:
      stradd(&str, "lvalue: ");
      str = svalue_to_string(obj->u.lvalue, str, indent+2, trailing);
      break;
    case T_NUMBER:
      numadd(&str, obj->u.number);
      break;
#ifdef FLOATS
    case T_FLOAT:
    {
      char s[80];

      sprintf(s, "%g", READ_DOUBLE(obj) );
      stradd(&str, s);
      break;
    }
#endif
    case T_STRING:
      stradd(&str, "\"");
      stradd(&str, obj->u.string);
      stradd(&str, "\"");
      break;
    case T_QUOTED_ARRAY:
    {
      i = obj->x.quotes;
      do {
	stradd(&str, "\'");
      } while (--i);
    }
    /* fall through */
    case T_POINTER:
      if (!(obj->u.vec->size)) {
        stradd(&str, "({ })");
      } else {
        stradd(&str, "({ /* sizeof() == ");
        numadd(&str, obj->u.vec->size);
        stradd(&str, " */\n");
        for (i=0; i<(obj->u.vec->size)-1; i++)
          str = svalue_to_string(&(obj->u.vec->item[i]), str, indent+2, 1);
        str = svalue_to_string(&(obj->u.vec->item[i]), str, indent+2, 0);
        stradd(&str, "\n");
        add_indent(&str, indent);
        stradd(&str, "})");
      }
      break;
#ifdef MAPPINGS
    case T_MAPPING:
    {
	void check_map_for_destr PROT((struct mapping *));
	void walk_mapping PROT((
            struct mapping *,
            void (*)(struct svalue *, struct svalue *, char *),
            char *
	));

      struct stsf_locals locals;

      stradd(&str, "([\n");
      locals.spb = str;
      locals.indent = indent + 2;
      locals.num_values = obj->u.map->num_values;
      check_map_for_destr(obj->u.map);
      walk_mapping(obj->u.map, svalue_to_string_filter, (char*)&locals);
      str = locals.spb;
      add_indent(&str, indent);
      stradd(&str, "])");
      break;
    }
#endif /* MAPPINGS */
    case T_OBJECT:
    {
      struct svalue *temp;

      stradd(&str, obj->u.ob->name);
      push_object(obj->u.ob);
      temp = apply_master_ob("object_name", 1);
      if (temp && (temp->type == T_STRING)) {
        stradd(&str, " (\"");
        stradd(&str, temp->u.string);
        stradd(&str, "\")");
      }
      /*
       * These flags aren't that useful...
       *
      if (obj->u.ob->flags & O_HEART_BEAT)	stradd(&str, " (hb)");
      if (obj->u.ob->flags & O_IS_WIZARD)	stradd(&str, " (wiz)");
      if (obj->u.ob->flags & O_ENABLE_COMMANDS)	stradd(&str, " (enabled)");
      if (obj->u.ob->flags & O_CLONE)		stradd(&str, " (clone)");
      if (obj->u.ob->flags & O_DESTRUCTED)	stradd(&str, " (destructed)");
      if (obj->u.ob->flags & O_SWAPPED)		stradd(&str, " (swapped)");
      if (obj->u.ob->flags & O_ONCE_INTERACTIVE) stradd(&str, " (x-activ)");
      if (obj->u.ob->flags & O_APPROVED)	stradd(&str, " (ok)");
      if (obj->u.ob->flags & O_RESET_STATE)	stradd(&str, " (reset)");
      if (obj->u.ob->flags & O_WILL_CLEAN_UP)	stradd(&str, " (clean up)");
       */
      break;
    }
    case T_SYMBOL:
      i = obj->x.quotes;
      do {
	stradd(&str, "\'");
      } while (--i);
      stradd(&str, obj->u.string);
      break;
    case T_CLOSURE:
    {
      int type;

      switch(type = obj->x.closure_type) {
	case CLOSURE_LFUN:
	{
	  struct lambda *l;
	  struct program *prog;
	  int ix;
	  uint32 flags;
	  char *function_name;

	  l = obj->u.lambda;
	  if (l->ob->flags & O_DESTRUCTED) {
	    stradd(&str, "<local function in destructed object>");
	    break;
	  }
	  stradd(&str, "#'");
	  stradd(&str, l->ob->name);
	  stradd(&str, "->");
	  prog = l->ob->prog;
	  ix = l->function.index;
	  flags = prog->functions[ix];
	  while (flags & NAME_INHERITED) {
	    struct inherit *inheritp;

	    inheritp = &prog->inherit[flags & INHERIT_MASK];
            ix -= inheritp->function_index_offset;
            prog = inheritp->prog;
            flags = prog->functions[ix];
	  }
          memcpy(
            (char *)&function_name,
	    prog->program + (flags & FUNSTART_MASK) - 1 - sizeof function_name,
            sizeof function_name
          );
	  stradd(&str, function_name);
	  break;
	}
	case CLOSURE_IDENTIFIER:
	{
	  struct lambda *l;

	  l = obj->u.lambda;
	  if (l->ob->flags & O_DESTRUCTED) {
	    stradd(&str, "<local variable in destructed object>");
	    break;
	  }
	  stradd(&str, "#'");
	  stradd(&str, l->ob->name);
	  stradd(&str, "->");
	  stradd(&str, l->ob->prog->variable_names[l->function.index].name);
	  break;
	}
	default:
	  if (type < 0) {
	    switch(type & -0x0800) {
	      case CLOSURE_OPERATOR:
	      {
		char *s = 0;
		switch(type - CLOSURE_OPERATOR) {
		  case F_POP_VALUE-F_OFFSET:
		    s = ",";
		    break;
		  case F_BBRANCH_WHEN_NON_ZERO-F_OFFSET:
		    s = "do";
		    break;
		  case F_BBRANCH_WHEN_ZERO-F_OFFSET:
		    s = "while";
		    break;
		  case F_BRANCH_WHEN_ZERO-F_OFFSET:
		    s = "?";
		    break;
		  case F_BRANCH_WHEN_NON_ZERO-F_OFFSET:
		    s = "?!";
		    break;
		  case F_RANGE-F_OFFSET:
		    s = "[..]";
		    break;
		  case F_NR_RANGE-F_OFFSET:
		    s = "[..<]";
		    break;
		  case F_RR_RANGE-F_OFFSET:
		    s = "[<..<]";
		    break;
		  case F_RN_RANGE-F_OFFSET:
		    s = "[<..]";
		    break;
		  case F_MAP_INDEX-F_OFFSET:
		    s = "[,]";
		    break;
		  case F_EXTRACT2-F_OFFSET:
		    s = "[..";
		    break;
		  case F_EXTRACT_LVALUE-F_OFFSET:
		    s = "[<..";
		    break;
		}
		if (s) {
		    stradd(&str, "#'");
		    stradd(&str, s);
		    break;
		}
		type += CLOSURE_EFUN - CLOSURE_OPERATOR;
	      }
	      /* default action for operators: fall through to efun case . */
	      case CLOSURE_EFUN:
		stradd(&str, "#'");
		stradd(&str, instrs[type - CLOSURE_EFUN].name);
		break;
	      case CLOSURE_SIMUL_EFUN:
	      {
		extern struct function *simul_efunp;

		stradd(&str, "#'");
		stradd(&str, simul_efunp[type - CLOSURE_SIMUL_EFUN].name);
		break;
	      }
	    }
	    break;
	  }
	case CLOSURE_LAMBDA:
	case CLOSURE_BOUND_LAMBDA:
	case CLOSURE_UNBOUND_LAMBDA:
	case CLOSURE_PRELIMINARY:
	{
	  static char s[] = "<lambda 0x012345>6789abcdef";

	  sprintf(s+10, "%x>", (p_int)obj->u.lambda);
	  stradd(&str, s);
	  break;
	}
      }
      break;
    }
    default:
      stradd(&str, "!ERROR: GARBAGE SVALUE!");
  } /* end of switch (obj->type) */
  if (trailing) stradd(&str, ",\n");
  return str;
} /* end of svalue_to_string() */

/*
 * Adds the string "str" to the buff after justifying it within "fs".
 * "trailing" is a flag which is set if trailing justification is to be done.
 * "str" is unmodified.  trailing is, of course, ignored in the case
 * of right justification.
 */
void add_justified(str, len, pad, fs, finfo, trailing)
  char *str, *pad;
  int len;
  int fs;
  format_info finfo;
  short int trailing;
{
  int i;

  switch(finfo & INFO_J) {
    case INFO_J_LEFT:
      ADD_STRN(str, len)
      if (trailing)
	ADD_PADDING(pad, fs - len)
      break;
    case INFO_J_CENTRE:
      ADD_PADDING(pad, (fs - len + 1) >> 1)
      ADD_STRN(str, len)
      if (trailing)
	ADD_PADDING(pad, (fs - len) >> 1)
      break;
    default: { /* std (s)printf defaults to right justification */
      ADD_PADDING(pad, fs - len)
      ADD_STRN(str, len)
    }
  }
} /* end of add_justified() */

/*
 * Adds "column" to the buffer.
 * Returns 0 is column not finished.
 * Returns 1 if column completed.
 * Returns 2 if column completed has a \n at the end.
 */
int add_column(column, trailing)
  cst **column;
  short int trailing;
{
  register unsigned int done;
  mp_int length;
  unsigned int save;
#define COL (*column)
  char *COL_D = COL->d.col;
  char *p;

  length = COL->pres;
  for (p = COL_D; length && *p && *p !='\n'; p++, length--);
  done = p - COL_D;
  if (*p && *p !='\n') {
    save = done;
    for (; ; done--,p--) {
	/* handle larger than column size words... */
	if (!done) {
	    done = save - 1;
	    p += save;
	    break;
	}
	if (*p == ' ')
	    break;
     }
  }
  add_justified(COL_D, p - COL_D, COL->pad, COL->size, COL->info,
                (trailing || (COL->next)));
  COL_D += done; /* inc'ed below ... */
  /*
   * if this or the next character is a NULL then take this column out
   * of the list.
   */
  if (!(*COL_D) || !(*(++COL_D))) {
    cst *temp;
    int ret;

    if (*(COL_D-1) == '\n') ret = 2; else ret = 1;
    temp = COL->next;
    xfree((char *)COL);
    COL = temp;
    return ret;
  }
  COL->d.col = COL_D;
  return 0;
} /* end of add_column() */

/*
 * Adds "table" to the buffer.
 * Returns 0 if table not completed.
 * Returns 1 if table completed.
 */
int add_table(table, trailing)
  cst **table;
  short int trailing;
{
  register unsigned int done, i;
#define TAB (*table)
#define TAB_D (TAB->d.tab[i])

  for (i=0; i < TAB->nocols && TAB_D; i++) {
    for (done=0;(TAB_D[done])&&(TAB_D[done] != '\n');done++);
    add_justified(TAB_D, done, TAB->pad, TAB->size, TAB->info, 
                  (trailing || (i < TAB->nocols-1) || (TAB->next)));
    TAB_D += done; /* inc'ed next line ... */
    if (!(*TAB_D) || !(*(++TAB_D))) TAB_D = 0;
  }
  if (trailing && i < TAB->nocols) {
    done = TAB->size;
    for (; i < TAB->nocols; i++)
      ADD_CHARN(' ', done)
  }
  if (!TAB->d.tab[0]) {
    cst *temp;

    temp = TAB->next;
    xfree((char *)TAB->d.tab);
    xfree((char *)TAB);
    TAB = temp;
    return 1;
  }
  return 0;
} /* end of add_table() */

/*
 * THE (s)printf() function.
 * It returns a pointer to it's internal buffer (or a string in the text
 * segment) thus, the string must be copied if it has to survive after
 * this function is called again, or if it's going to be modified (esp.
 * if it risks being free()ed).
 */
char *string_print_formatted(format_str, argc, argv)
  char *format_str;
  int argc;
  struct svalue *argv;
{
  format_info finfo;
  char format_char;
  savechars *saves = 0;	/* chars to restore */
  cst *csts;		/* list of columns/tables to be done */
  /* It is important that the address of csts is taken at least once, so
   * that it will have the correct value at error recovery.
   */
  struct svalue *carg;	/* current arg */
  unsigned int nelemno;	/* next offset into array */
  unsigned int fpos;	/* position in format_str */
  unsigned int arg;	/* current arg number */
  unsigned int fs;	/* field size */
  int pres;		/* presision */
  unsigned int i;
  char *pad;		/* fs pad string */
  struct svalue clean;  /* the address of clean is taken somewhere below. This
			   is essential, to prevent putting it into a register,
			   which would make longjmp() dangerous. */

  clean.u.string = 0;
  if ((i = setjmp(error_jmp))) { /* error handling */
    extern struct svalue const1;
    static char error_prefix[] = "ERROR: (s)printf(): ";
    char *err;
    cst *tcst;

    /* Amylaar: restore also in case of errors.... */
    /* Use the address of saves to be sure it is not put into a register.
     * This must be done in a way that no optimizing compiler will drop.
     */
    if (const1.u.number == 0) {
	xfree( (char *)&saves );
    }
    while (saves) {
      savechars *tmp;
      *(saves->where) = saves->what;
      tmp = saves;
      saves = saves->next;
      xfree((char *)tmp);
    }
    if (clean.u.string)
      xfree(clean.u.string);
    while (tcst = csts) {
	csts = tcst->next;
	if (tcst->info & INFO_TABLE)
	    xfree((char *)tcst->d.tab);
	xfree((char *)tcst);
    }
    switch(i & ERR_ID_NUMBER) {
      default:
#ifdef DEBUG
	fatal("undefined (s)printf() error 0x%X\n", i);
#endif
      case ERR_BUFF_OVERFLOW:
        err = "BUFF_SIZE overflowed...";
        break;
      case ERR_TO_FEW_ARGS:
        err = "More arguments specified than passed.";
        break;
      case ERR_INVALID_STAR:
        err = "Incorrect argument type to *.";
        break;
      case ERR_PRES_EXPECTED:
        err = "Expected presision not found.";
        break;
      case ERR_INVALID_FORMAT_STR:
        err = "Error in format string.";
        break;
      case ERR_INCORRECT_ARG:
	err = "incorrect argument type to %%%c.";
        break;
      case ERR_CST_REQUIRES_FS:
        err = "Column/table mode requires a field size.";
        break;
      case ERR_UNDEFINED_TYPE:
        err = "!feature - undefined type!";
        break;
      case ERR_QUOTE_EXPECTED:
        err = "Quote expected in format string.";
        break;
      case ERR_UNEXPECTED_EOS:
        err = "Unexpected end of format string.";
        break;
      case ERR_NULL_PS:
        err = "Null pad string specified.";
        break;
      case ERR_ARRAY_EXPECTED:
	err = "Array expected.";
	break;
    }
    strcpy(buff, error_prefix);
    sprintf(buff + sizeof(error_prefix) - 1, err, EXTRACT_ERR_ARGUMENT(i) );
    strcat(buff, "\n");
#ifdef RETURN_ERROR_MESSAGES
    {
	int line;
	char *file;

	line = get_line_number_if_any(&file);
	fprintf(stderr, "%s:%d: %s", file,
                                 line, buff);
    }
    return buff;
#else
    error(buff);
#endif /* RETURN_ERROR_MESSAGES */
  }
  arg = -1;
  bpos = 0;
  line_start = 0;
  csts = 0;
  for (fpos=0; 1; fpos++) {
    if ((format_str[fpos] == '\n') || (!format_str[fpos])) {
      int column_stat = 0;

      if (!csts) {
        if (!format_str[fpos]) break;
        ADD_CHAR('\n');
        line_start = bpos;
        continue;
      }
      ADD_CHAR('\n');
      line_start = bpos;
      while (csts) {
        cst **temp;

        temp = &csts;
        while (*temp) {
          if ((*temp)->info & INFO_COLS) {
            if (*((*temp)->d.col-1) != '\n')
              while (*((*temp)->d.col) == ' ') (*temp)->d.col++;
	    i = (*temp)->start - (bpos - line_start);
	    ADD_CHARN(' ', i);
            column_stat = add_column(temp, 0);
            if (!column_stat) temp = &((*temp)->next);
          } else {
	    i = (*temp)->start - (bpos - line_start);
	    ADD_CHARN(' ', i);
            if (!add_table(temp, 0)) temp = &((*temp)->next);
          }
        } /* of while (*temp) */
        if (csts || format_str[fpos] == '\n')
          ADD_CHAR('\n');
        line_start = bpos;
      } /* of while (csts) */
      if (column_stat == 2) ADD_CHAR('\n');
      if (!format_str[fpos]) break;
      continue;
    }
    if (format_str[fpos] == '%') {
      if (format_str[fpos+1] == '%') {
        ADD_CHAR('%');
        fpos++;
        continue;
      }
      GET_NEXT_ARG;
      fs = 0;
      pres = 0;
      pad = " ";
      finfo = 0;
      for (fpos++; !(finfo & INFO_T); fpos++) {
        if (!format_str[fpos]) {
          finfo |= INFO_T_ERROR;
          break;
        }
        if (((format_str[fpos] >= '0') && (format_str[fpos] <= '9'))
            || (format_str[fpos] == '*')) {
          if (pres == -1) { /* then looking for pres */
            if (format_str[fpos] == '*') {
              if (carg->type != T_NUMBER)
                ERROR(ERR_INVALID_STAR);
              pres = carg->u.number;
              GET_NEXT_ARG;
              continue;
            }
            pres = format_str[fpos] - '0';
            for (fpos++;
                 (format_str[fpos]>='0')&&(format_str[fpos]<='9'); fpos++) {
              pres = pres*10 + format_str[fpos] - '0';
            }
          } else { /* then is fs (and maybe pres) */
            if ((format_str[fpos] == '0') && (((format_str[fpos+1] >= '1')
                && (format_str[fpos+1] <= '9')) || (format_str[fpos+1] == '*')))
              pad = "0";
            else {
              if (format_str[fpos] == '*') {
                if (carg->type != T_NUMBER)
                  ERROR(ERR_INVALID_STAR);
                if ((int)(fs = carg->u.number) < 0) {
		    fs = -fs;
		    finfo |= INFO_J_LEFT;
		}
                if (pres == -2) pres = fs; /* colon */
                GET_NEXT_ARG;
                continue;
              }
              fs = format_str[fpos] - '0';
            }
            for (fpos++;
                 (format_str[fpos]>='0')&&(format_str[fpos]<='9'); fpos++) {
              fs = fs*10 + format_str[fpos] - '0';
            }
            if (pres == -2) { /* colon */
              pres = fs;
            }
          }
          fpos--; /* bout to get incremented */
          continue;
        }
        switch (format_str[fpos]) {
          case ' ': finfo |= INFO_PP_SPACE; break;
          case '+': finfo |= INFO_PP_PLUS; break;
          case '-': finfo |= INFO_J_LEFT; break;
          case '|': finfo |= INFO_J_CENTRE; break;
          case '@': finfo |= INFO_ARRAY; break;
          case '=': finfo |= INFO_COLS; break;
          case '#': finfo |= INFO_TABLE; break;
          case '.': pres = -1; break;
          case ':': pres = -2; break;
          case '%': finfo |= INFO_T_NULL; break; /* never reached */
          case 'O': finfo |= INFO_T_LPC; break;
          case 's': finfo |= INFO_T_STRING; break;
          case 'i': finfo |= INFO_T_INT; format_char = 'd'; break;
          case 'd':
          case 'c':
          case 'o':
          case 'x':
          case 'X':
	    format_char = format_str[fpos]; finfo |= INFO_T_INT; break;
          case 'f':
          case 'F':
          case 'g':
          case 'G':
          case 'e':
          case 'E':
	    format_char = format_str[fpos]; finfo |= INFO_T_FLOAT; break;
          case '\'':
            pad = &(format_str[++fpos]);
            while (1) {
              if (!format_str[fpos]) ERROR(ERR_UNEXPECTED_EOS);
              if (format_str[fpos] == '\\') {
                if (!format_str[fpos+1]) ERROR(ERR_UNEXPECTED_EOS);
                fpos += 2;
                continue;
              }
              if (format_str[fpos] == '\'') {
                if (format_str+fpos == pad) ERROR(ERR_NULL_PS);
                SAVE_CHAR(format_str+fpos);
                format_str[fpos] = '\0';
                break;
              }
              fpos++;
            }
            break;
          default: finfo |= INFO_T_ERROR;
        }
      } /* end of for () */
      if (pres < 0)
        ERROR(ERR_PRES_EXPECTED);
      /*
       * now handle the different arg types...
       */
      if (finfo & INFO_ARRAY) {
	if (carg->type != T_POINTER)
	  ERROR(ERR_ARRAY_EXPECTED);
        if (carg->u.vec->size == 0) {
          fpos--; /* 'bout to get incremented */
          continue;
        }
        carg = (argv+arg)->u.vec->item;
        nelemno = 1; /* next element number */
      }
      while (1) {
	switch(finfo & INFO_T) {
	  case INFO_T_ERROR:
	    ERROR(ERR_INVALID_FORMAT_STR);
	  case INFO_T_NULL:
	  {
	    /* never reached... */
            fprintf(stderr, "%s: (s)printf: INFO_T_NULL.... found.\n",
                  current_object->name);
            ADD_CHAR('%');
	    break;
	  }
	  case INFO_T_LPC:
	  {
	    struct sprintf_buffer *b;
#define CLEANSIZ 0x200

	    if (clean.u.string)
	      xfree(clean.u.string);
            clean.type = T_STRING;
            clean.x.string_type = STRING_MALLOC;
            clean.u.string = (char *)xalloc(CLEANSIZ);
            clean.u.string[0] = '\0';
	    b = (struct sprintf_buffer *)
		 ( clean.u.string+CLEANSIZ-sizeof(struct sprintf_buffer) );
	    b->offset = -CLEANSIZ+sizeof(struct sprintf_buffer);
	    b->size = CLEANSIZ;
	    b->start = &clean.u.string;
            svalue_to_string(carg, b, 0, 0);
            carg = &clean;			/* be sure not to remove the
						   last address reference to
						   clean */
	    /* fall through */
         }
	 case INFO_T_STRING:
	 {
          int slen;

          if (carg->type != T_STRING)
            ERROR1(ERR_INCORRECT_ARG, 's');
          slen = strlen(carg->u.string);
          if (finfo & (INFO_COLS | INFO_TABLE) ) {
            cst **temp;

            if (!fs)
              ERROR(ERR_CST_REQUIRES_FS);

            temp = &csts;
            while (*temp) temp = &((*temp)->next);
            if (finfo & INFO_COLS) {
              *temp = (cst *)xalloc(sizeof(cst));
              (*temp)->next = 0;
              (*temp)->d.col = carg->u.string;
              (*temp)->pad = pad;
              (*temp)->size = fs;
              (*temp)->pres = (pres) ? pres : fs;
              (*temp)->info = finfo;
              (*temp)->start = bpos - line_start;
              if ((add_column(temp, (((format_str[fpos] != '\n')
                    && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY)
                      && (nelemno < (argv+arg)->u.vec->size)))) == 2)
                  && !format_str[fpos])
	      {
                ADD_CHAR('\n');
              }
            } else { /* (finfo & INFO_TABLE) */
              unsigned int n, len, max;
	      char c, *s, *start;

#define TABLE carg->u.string
              (*temp) = (cst *)xalloc(sizeof(cst));
              (*temp)->pad = pad;
              (*temp)->info = finfo;
              (*temp)->start = bpos - line_start;
              (*temp)->next = 0;
              max = len = 0;
              n = 1;
	      s = TABLE;
	      if (c = *(start = s)) for (;;) {
		if (c != '\n') {
		  if (c = *++s)
		    continue;
		  else
		    break;
		}
		len = s - start;
                if (len > max) max = len;
		n++;
		if (c = *(start = ++s)) {
		  continue;
		}
		n--;
		break;
	      }
              if (pres) {
                (*temp)->size = fs/pres;
              } else {
		len = s - start;
                if (len > max) max = len; /* the null terminated word */
                pres = fs/(max+2); /* at least two separating spaces */
                if (!pres) pres = 1;
                (*temp)->size = fs/pres;
              }
              len = n/pres; /* length of average column */
              if (n < pres) pres = n;
              if (len*pres < n) len++;
              if (len > 1 && n%pres) pres -= (pres - n%pres)/len;
              (*temp)->d.tab = (char **)xalloc(pres*sizeof(char *));
              (*temp)->nocols = pres; /* heavy sigh */
              (*temp)->d.tab[0] = TABLE;
              if (pres == 1) goto add_table_now;
              i = 1; /* the next column number */
              n = 0; /* the current "word" number in this column */
              for (fs = 0; TABLE[fs]; fs++) { /* throwing away fs... */
                if (TABLE[fs] == '\n') {
                  if (++n >= len) {
                    SAVE_CHAR(((TABLE)+fs));
                    TABLE[fs] = '\0';
                    (*temp)->d.tab[i++] = TABLE+fs+1;
                    if (i >= pres) goto add_table_now;
                    n = 0;
                  }
                }
              }
add_table_now:
              add_table(temp, (((format_str[fpos] != '\n')
                && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY)
                  && (nelemno < (argv+arg)->u.vec->size))));
            }
          } else { /* not column or table */
            if (pres && pres<slen) {
              slen = pres;
            }
            if (fs && fs>slen) {
              add_justified(carg->u.string, slen, pad, fs, finfo,
          (((format_str[fpos] != '\n') && (format_str[fpos] != '\0'))
          || ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size)))
          || carg->u.string[slen-1] != '\n');
            } else {
	      ADD_STRN(carg->u.string, slen)
            }
          }
	  break;
	 }
	 case INFO_T_INT:
	 case INFO_T_FLOAT:
	 {
	    char cheat[6];
	    char temp[100];
	    int tmpl;
  
	    *cheat = '%';
	    i = 1;
	    switch (finfo & INFO_PP) {
	      case INFO_PP_SPACE: cheat[i++] = ' '; break;
	      case INFO_PP_PLUS:  cheat[i++] = '+'; break;
	    }
	    if ((finfo & INFO_T) == INFO_T_FLOAT) {
	      if (carg->type != T_FLOAT) { /* sigh... */
		ERROR1(ERR_INCORRECT_ARG, format_char);
	      }
	      cheat[i++] = '.';
	      cheat[i++] = '*';
	      cheat[i++] = format_char;
	      cheat[i] = '\0';
	      if (pres > sizeof(temp) - 12) {
		pres = sizeof(temp) - 12;
	      }
	      sprintf(temp, cheat, pres, READ_DOUBLE(carg));
	      tmpl = strlen(temp);
	    } else {
	      if (carg->type != T_NUMBER) { /* sigh... */
		ERROR1(ERR_INCORRECT_ARG, format_char);
	      }
	      cheat[i++] = format_char;
	      cheat[i] = '\0';
	      sprintf(temp, cheat, carg->u.number);
	      tmpl = strlen(temp);
	      if (pres && tmpl > pres) tmpl = pres; /* well.... */
	    }
	    if (tmpl < fs)
	      add_justified(temp, tmpl, pad, fs, finfo,
		(((format_str[fpos] != '\n') && (format_str[fpos] != '\0')) ||
		 ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size)))
	      );
	    else
	      ADD_STRN(temp, tmpl)
	    break;
	  }
	  default:	/* type not found */
	    ERROR(ERR_UNDEFINED_TYPE);
	}
        if (!(finfo & INFO_ARRAY)) break;
        if (nelemno >= (argv+arg)->u.vec->size) break;
        carg = (argv+arg)->u.vec->item+nelemno++;
      } /* end of while (1) */
      fpos--; /* bout to get incremented */
      continue;
    }
    ADD_CHAR(format_str[fpos]);
  } /* end of for (fpos=0; 1; fpos++) */
  ADD_CHAR('\0');
  while (saves) {
    savechars *tmp;
    *(saves->where) = saves->what;
    tmp = saves;
    saves = saves->next;
    xfree((char *)tmp);
  }
  if (clean.u.string)
    xfree(clean.u.string);
  return buff;
} /* end of string_print_formatted() */

#endif /* defined(F_SPRINTF) || defined(F_PRINTF) */