mud/fluffos-2.23-ds03/sprintf.c
2020-09-06 05:43:07 -07:00

1334 lines
47 KiB
C

/*
* 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).
* 2/28/93: float support for MudOS added by jacques/blackthorn
*
* 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 precision of n, simple strings truncate after this (if precision is
* greater than field size, then field size = precision), tables use
* precision to specify the number of columns (if precision not specified
* then tables calculate a best fit), all other types ignore this.
* ":"n n specifies the fs _and_ the precision, 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.
* "f" floating point value.
* "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).
*/
#include "std.h"
#include "sprintf.h"
#include "efuns_incl.h"
#include "simul_efun.h"
#include "lex.h"
#include "stralloc.h"
#include "master.h"
#if defined(F_SPRINTF) || defined(F_PRINTF)
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;
* 1000 : integer;
* 1001 : char;
* 1010 : octal;
* 1011 : hex;
* 1100 : HEX;
* 1101 : 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 0x8
#define INFO_T_CHAR 0x9
#define INFO_T_OCT 0xA
#define INFO_T_HEX 0xB
#define INFO_T_C_HEX 0xC
#define INFO_T_FLOAT 0xD
#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 ERR_BUFF_OVERFLOW 0x1 /* buffer overflowed */
#define ERR_TOO_FEW_ARGS 0x2 /* more arguments spec'ed than passed */
#define ERR_INVALID_STAR 0x3 /* invalid arg to * */
#define ERR_PRES_EXPECTED 0x4 /* expected precision not found */
#define ERR_INVALID_FORMAT_STR 0x5 /* error in format string */
#define ERR_INCORRECT_ARG_S 0x6 /* invalid arg to %s */
#define ERR_CST_REQUIRES_FS 0x7 /* field size not given for c/t */
#define ERR_BAD_INT_TYPE 0x8 /* bad integer type... */
#define ERR_UNDEFINED_TYPE 0x9 /* undefined type found */
#define ERR_QUOTE_EXPECTED 0xA /* expected ' not found */
#define ERR_UNEXPECTED_EOS 0xB /* fs terminated unexpectedly */
#define ERR_NULL_PS 0xC /* pad string is null */
#define ERR_ARRAY_EXPECTED 0xD /* Yep! You guessed it. */
#define ERR_RECOVERY_ONLY 0xE /* err msg already done...just
* recover */
#define ADD_CHAR(x) {\
if (sprintf_state->obuff.real_size == MAX_STRING_LENGTH) ERROR(ERR_BUFF_OVERFLOW); \
outbuf_addchar(&(sprintf_state->obuff), x);\
}
#define GET_NEXT_ARG {\
if (++sprintf_state->cur_arg >= argc) ERROR(ERR_TOO_FEW_ARGS); \
carg = (argv + sprintf_state->cur_arg);\
}
typedef struct {
const char *what;
int len;
} pad_info_t;
typedef struct {
const char *start;
const char *cur;
} tab_data_t;
/* slash here means 'or' */
typedef struct ColumnSlashTable {
union CSTData {
const char *col; /* column data */
tab_data_t *tab; /* table data */
} d; /* d == data */
unsigned short int nocols; /* number of columns in table *sigh* */
pad_info_t *pad;
unsigned int start; /* starting cursor position */
unsigned int size; /* column/table width */
unsigned int remainder; /* extra space needed to fill out to width */
int pres; /* precision */
format_info info; /* formatting data */
struct ColumnSlashTable *next;
} cst; /* Columns Slash Tables */
typedef struct _sprintf_state {
outbuffer_t obuff;
cst *csts;
SIGNED int cur_arg;
svalue_t clean;
struct _sprintf_state *next;
} sprintf_state_t;
static sprintf_state_t *sprintf_state = NULL;
static void numadd (outbuffer_t *, long num);
static void add_space (outbuffer_t *, int indent);
static void add_justified (const char *str, int slen, pad_info_t *pad, int fs, format_info finfo, short int trailing);
static int add_column (cst ** column, int trailing);
static int add_table (cst ** table);
#define ERROR(x) sprintf_error(x, 0)
static void pop_sprintf_state (void) {
sprintf_state_t *state;
state = sprintf_state;
sprintf_state = sprintf_state->next;
if (state->obuff.buffer) {
FREE_MSTR(state->obuff.buffer);
}
while (state->csts) {
cst *next = state->csts->next;
if (!(state->csts->info & INFO_COLS) && state->csts->d.tab) {
FREE(state->csts->d.tab);
}
FREE(state->csts);
state->csts = next;
}
if (state->clean.type != T_NUMBER) {
free_svalue(&(state->clean), "pop_sprintf_state");
}
FREE(state);
}
static void push_sprintf_state (void) {
sprintf_state_t *state;
state = ALLOCATE(sprintf_state_t, TAG_TEMPORARY, "push_sprintf_state");
outbuf_zero(&(state->obuff));
state->csts = NULL;
state->cur_arg = -1;
state->clean.type = T_NUMBER;
state->clean.u.number = 0;
state->next = sprintf_state;
sprintf_state = state;
}
/* Signal an error. Note that we call error, so this routine never returns.
* Anything that has been allocated should be somewhere it can be found and
* freed later.
*/
static void sprintf_error (int which, char * premade) {
char lbuf[2048];
const char *err;
switch (which) {
case ERR_BUFF_OVERFLOW:
err = "BUFF_SIZE overflowed...";
break;
case ERR_TOO_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 precision not found.";
break;
case ERR_INVALID_FORMAT_STR:
err = "Error in format string.";
break;
case ERR_INCORRECT_ARG_S:
err = "Incorrect argument to type %%s.";
break;
case ERR_CST_REQUIRES_FS:
err = "Column/table mode requires a field size.";
break;
case ERR_BAD_INT_TYPE:
err = "!feature - bad integer type!";
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;
case ERR_RECOVERY_ONLY:
err = premade;
break;
default:
err = "undefined error in (s)printf!\n";
break;
}
sprintf(lbuf, "(s)printf(): %s (arg: %d)\n", err, sprintf_state->cur_arg);
error(lbuf);
}
static void numadd (outbuffer_t * outbuf, long num)
{
long i, num_l, /* length of num as a string */
nve; /* true if num negative */
int space;
int chop;
char *p;
if (num < 0) {
/* Beek: yes, it's possible for num < 0, and num * -1 < 0. */
/* Beek: This shouldn't be a hardcoded const (assumes int is 4 bytes)*/
/* Wodan: indeed! */
#if SIZEOF_LONG==4
num = (num * -1) & 0x7fffffff;
#else
num = (num * -1) & 0x7fffffffffffffff;
#endif
nve = 1;
} else
nve = 0;
for (i = num / 10, num_l = nve + 1; i; i /= 10, num_l++);
if ((space = outbuf_extend(outbuf, num_l))) {
chop = num_l - space;
while (chop--)
num /= 10; /* lose that last digits that got chopped */
p = outbuf->buffer + outbuf->real_size;
outbuf->real_size += space;
p[space] = 0;
if (nve) {
*p++ = '-';
space--;
}
while (space--) {
p[space] = (num % 10) + '0';
num /= 10;
}
}
} /* end of numadd() */
static void add_space (outbuffer_t * outbuf, int indent)
{
int l;
if ((l = outbuf_extend(outbuf, indent))) {
memset(outbuf->buffer + outbuf->real_size, ' ', l);
*(outbuf->buffer + outbuf->real_size + l) = 0;
outbuf->real_size += l;
}
}
/*
* Converts any LPC datatype into an arbitrary string format
* and returns a pointer to this string.
* Scary number of parameters for a recursive function.
*/
void svalue_to_string (svalue_t * obj, outbuffer_t * outbuf, int indent, int trailing, int indent2)
{
int i;
/* prevent an infinite recursion on self-referential structures */
if (indent > 20) {
outbuf_add(outbuf, "...");
return;
}
if (!indent2)
add_space(outbuf, indent);
switch ((obj->type & ~T_FREED)) {
case T_INVALID:
outbuf_add(outbuf, "T_INVALID");
break;
case T_LVALUE:
outbuf_add(outbuf, "lvalue: ");
svalue_to_string(obj->u.lvalue, outbuf, indent + 2, trailing, 0);
break;
case T_REF:
if(!obj->u.ref->lvalue)
kill_ref(obj->u.ref);
else {
outbuf_add(outbuf, "ref: ");
svalue_to_string(obj->u.ref->lvalue, outbuf, indent + 2, trailing, 0);
}
break;
case T_NUMBER:
numadd(outbuf, obj->u.number);
break;
case T_REAL:
outbuf_addv(outbuf, "%f", obj->u.real);
break;
case T_STRING:
outbuf_add(outbuf, "\"");
outbuf_add(outbuf, obj->u.string);
outbuf_add(outbuf, "\"");
break;
case T_CLASS:
{
int n = obj->u.arr->size;
outbuf_add(outbuf, "CLASS( ");
numadd(outbuf, n);
outbuf_add(outbuf, n == 1 ? " element\n" : " elements\n");
for (i = 0; i < (obj->u.arr->size) - 1; i++)
svalue_to_string(&(obj->u.arr->item[i]), outbuf,
indent + 2, 1, 0);
if(obj->u.arr->size)
svalue_to_string(&(obj->u.arr->item[i]), outbuf,
indent + 2, 0, 0);
outbuf_add(outbuf, "\n");
add_space(outbuf, indent);
outbuf_add(outbuf, " )");
break;
}
case T_ARRAY:
if (!(obj->u.arr->size)) {
outbuf_add(outbuf, "({ })");
} else {
outbuf_add(outbuf, "({ /* sizeof() == ");
numadd(outbuf, obj->u.arr->size);
outbuf_add(outbuf, " */\n");
for (i = 0; i < (obj->u.arr->size) - 1; i++)
svalue_to_string(&(obj->u.arr->item[i]), outbuf, indent + 2, 1, 0);
svalue_to_string(&(obj->u.arr->item[i]), outbuf, indent + 2, 0, 0);
outbuf_add(outbuf, "\n");
add_space(outbuf, indent);
outbuf_add(outbuf, "})");
}
break;
#ifndef NO_BUFFER_TYPE
case T_BUFFER:
outbuf_add(outbuf, "<buffer>");
break;
#endif
case T_FUNCTION:
{
svalue_t tmp;
object_t *ob;
tmp.type = T_ARRAY;
outbuf_add(outbuf, "(: ");
switch (obj->u.fp->hdr.type) {
case FP_LOCAL | FP_NOT_BINDABLE:
ob = obj->u.fp->hdr.owner;
if (!ob || ob->flags & O_DESTRUCTED) {
outbuf_add(outbuf, "0");
break;
}
outbuf_add(outbuf, function_name(ob->prog,
obj->u.fp->f.local.index));
break;
case FP_SIMUL:
outbuf_add(outbuf, simuls[obj->u.fp->f.simul.index].func->funcname);
break;
case FP_FUNCTIONAL:
case FP_FUNCTIONAL | FP_NOT_BINDABLE:
{
char buf[10];
int n = obj->u.fp->f.functional.num_arg;
outbuf_add(outbuf, "<code>(");
for (i=1; i < n; i++) {
sprintf(buf, "$%i, ", i);
outbuf_add(outbuf, buf);
}
if (n) {
sprintf(buf, "$%i", n);
outbuf_add(outbuf, buf);
}
outbuf_add(outbuf, ")");
break;
}
case FP_EFUN:
{
int i;
i = obj->u.fp->f.efun.index;
outbuf_add(outbuf, query_instr_name(i));
break;
}
}
if (obj->u.fp->hdr.args) {
for (i=0; i<obj->u.fp->hdr.args->size; i++) {
outbuf_add(outbuf, ", ");
svalue_to_string(&(obj->u.fp->hdr.args->item[i]), outbuf, indent, 0, 0);
}
}
}
outbuf_add(outbuf, " :)");
break;
case T_MAPPING:
if (!(obj->u.map->count)) {
outbuf_add(outbuf, "([ ])");
} else {
outbuf_add(outbuf, "([ /* sizeof() == ");
numadd(outbuf, obj->u.map->count);
outbuf_add(outbuf, " */\n");
for (i = 0; i <= obj->u.map->table_size; i++) {
mapping_node_t *elm;
for (elm = obj->u.map->table[i]; elm; elm = elm->next) {
svalue_to_string(&(elm->values[0]), outbuf, indent + 2, 0, 0);
outbuf_add(outbuf, " : ");
svalue_to_string(&(elm->values[1]), outbuf, indent + 4, 1, 1);
}
}
add_space(outbuf, indent);
outbuf_add(outbuf, "])");
}
break;
case T_OBJECT:
{
svalue_t *temp;
if (obj->u.ob->flags & O_DESTRUCTED) {
numadd(outbuf, 0);
break;
}
outbuf_addchar(outbuf, '/');
outbuf_add(outbuf, obj->u.ob->obname);
if (!max_eval_error && !too_deep_error) {
push_object(obj->u.ob);
temp = safe_apply_master_ob(APPLY_OBJECT_NAME, 1);
if (temp && temp != (svalue_t *) -1 && (temp->type == T_STRING)) {
outbuf_add(outbuf, " (\"");
outbuf_add(outbuf, temp->u.string);
outbuf_add(outbuf, "\")");
}
}
break;
}
default:
outbuf_addv(outbuf, "!ERROR: GARBAGE SVALUE: %x!", obj->type);
} /* end of switch (obj->type) */
if (trailing)
outbuf_add(outbuf, ",\n");
} /* end of svalue_to_string() */
static void add_pad (pad_info_t * pad, int len) {
char *p;
int padlen;
if (outbuf_extend(&(sprintf_state->obuff), len) < len)
ERROR(ERR_BUFF_OVERFLOW);
p = sprintf_state->obuff.buffer + sprintf_state->obuff.real_size;
sprintf_state->obuff.real_size += len;
p[len] = 0;
if (pad && (padlen = pad->len)) {
char *end;
const char *pstr = pad->what;
int i;
char c;
for (i = 0, end = p + len; p < end; i++) {
if (i == padlen)
i = 0;
if ((c = pstr[i]) == '\\') {
/* guaranteed to have a valid char next */
*p++ = pstr[++i];
} else
*p++ = c;
}
} else
memset(p, ' ', len);
}
INLINE_STATIC void add_nstr (const char * str, int len) {
if (outbuf_extend(&(sprintf_state->obuff), len) < len)
ERROR(ERR_BUFF_OVERFLOW);
memcpy(sprintf_state->obuff.buffer + sprintf_state->obuff.real_size, str, len);
sprintf_state->obuff.real_size += len;
sprintf_state->obuff.buffer[sprintf_state->obuff.real_size] = 0;
}
/*
* 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.
*/
static void add_justified (const char * str, int slen, pad_info_t * pad,
int fs, format_info finfo, short int trailing)
{
#ifdef USE_ICONV
int skip = 0;
int wide = 0;
const char *p2 = str + slen;
while (p2 > str){
if((*p2) & 0x80){
wide = 1;
skip++;
}else{
if(wide){
wide = 0;
skip--;
}
}
p2--;
}
if(wide)
skip--;
fs -= (slen - skip);
#else
fs -= slen;
#endif
if (fs <= 0) {
add_nstr(str, slen);
} else {
int i;
switch (finfo & INFO_J) {
case INFO_J_LEFT:
add_nstr(str, slen);
if (trailing)
add_pad(pad, fs);
break;
case INFO_J_CENTRE:
i = fs / 2 + fs % 2;
add_pad(pad, i);
add_nstr(str, slen);
if (trailing)
add_pad(pad, fs - i);
break;
case INFO_J_CENTRE | INFO_J_LEFT:
i = fs / 2;
add_pad(pad, i);
add_nstr(str, slen);
if (trailing)
add_pad(pad, fs - i);
break;
default:
/* std (s)printf defaults to right
* justification */
add_pad(pad, fs);
add_nstr(str, slen);
}
}
} /* 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.
*/
static int add_column (cst ** column, int trailing)
{
register unsigned int done;
char c;
int space = -1;
int ret;
cst *col = *column; /* always holds (*column) */
const char *col_d = col->d.col; /* always holds (col->d.col) */
done = 0;
#ifdef USE_ICONV
int width = 0;
#endif
/* find a good spot to break the line */
while ((c = col_d[done]) && c != '\n') {
if (c == ' ')
space = done;
#ifdef USE_ICONV
if(c & 0x80){
if(!(col_d[done+1] & 0x80) )
width++;
} else {
width++;
}
done++;
if (width == col->pres) {
#else
if (++done == col->pres) {
#endif
if (space != -1) {
c = col_d[done];
if (c != '\n' && c != ' ' && c)
done = space;
}
break;
}
}
add_justified(col_d, done, col->pad,
col->size, col->info, trailing || col->next);
col_d += done;
ret = 1;
if (*col_d == '\n') {
col_d++;
ret = 2;
}
col->d.col = col_d;
/*
* if the next character is a NULL then take this column out of
* the list.
*/
if (!(*col_d)) {
cst *temp;
temp = col->next;
if (col->pad)
FREE(col->pad);
FREE(col);
*column = temp;
return ret;
}
return 0;
} /* end of add_column() */
/*
* Adds "table" to the buffer.
* Returns 0 if table not completed.
* Returns 1 if table completed.
*/
static int add_table (cst ** table)
{
int done, i;
cst *tab = *table; /* always (*table) */
tab_data_t *tab_d = tab->d.tab; /* always tab->d.tab */
const char *tab_di; /* always tab->d.tab[i].cur */
int end;
#ifdef USE_ICONV
int width, tabwidth;
#endif
for (i = 0; i < tab->nocols && (tab_di = tab_d[i].cur); i++) {
end = tab_d[i + 1].start - tab_di - 1;
#ifdef USE_ICONV
width = 0;
#endif
for (done = 0; done != end && tab_di[done] != '\n'; done++)
#ifdef USE_ICONV
{
if(tab_di[done] & 0x80){
if(!(tab_di[done+1] & 0x80) )
width++;
} else
width++;
if(width == tab->size)
tabwidth = done+1;
}
add_justified(tab_di, (width > tab->size ? tabwidth : done),
tab->pad, tab->size, tab->info,
tab->pad || (i < tab->nocols - 1) || tab->next);
#else
;
add_justified(tab_di, (done > tab->size ? tab->size : done),
tab->pad, tab->size, tab->info,
tab->pad || (i < tab->nocols - 1) || tab->next);
#endif
if (done >= end - 1) {
tab_di = 0;
} else {
tab_di += done + 1; /* inc'ed next line ... */
}
tab_d[i].cur = tab_di;
}
if (tab->pad) {
while (i++ < tab->nocols) {
add_pad(tab->pad, tab->size);
}
add_pad(tab->pad, tab->remainder);
}
if (!tab_d[0].cur) {
cst *temp;
temp = tab->next;
if (tab->pad)
FREE(tab->pad);
if (tab_d)
FREE(tab_d);
FREE(tab);
*table = temp;
return 1;
}
return 0;
} /* end of add_table() */
static int get_curpos() {
char *p1, *p2;
if (!sprintf_state->obuff.buffer) return 0;
p1 = sprintf_state->obuff.buffer + sprintf_state->obuff.real_size - 1;
p2 = p1;
#ifdef USE_ICONV
int skip = 0;
int wide = 0;
while (p2 > sprintf_state->obuff.buffer && *p2 != '\n'){
if((*p2) & 0x80){
wide = 1;
skip++;
}else{
if(wide){
wide = 0;
skip--;
}
}
p2--;
}
if(wide)
skip--;
if (*p2 != '\n')
return p1 - p2 + 1 - skip;
else
return p1 - p2 - skip;
#else
while (p2 > sprintf_state->obuff.buffer && *p2 != '\n')
p2--;
if (*p2 != '\n')
return p1 - p2 + 1;
else
return p1 - p2;
#endif
}
/* We can't use a pointer to a local in a table or column, since it
* could get overwritten by another on the same line.
*/
static pad_info_t *make_pad (pad_info_t * p) {
pad_info_t *x;
if (p->len == 0) return 0;
x = ALLOCATE(pad_info_t, TAG_TEMPORARY, "make_pad");
x->what = p->what;
x->len = p->len;
return x;
}
/*
* 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 (const char * format_str, int argc, svalue_t * argv)
{
format_info finfo;
svalue_t *carg; /* current arg */
unsigned int nelemno = 0; /* next offset into array */
unsigned int fpos; /* position in format_str */
int fs; /* field size */
int pres; /* precision */
pad_info_t pad; /* fs pad string */
unsigned int i;
char *retvalue;
int last;
push_sprintf_state();
STACK_INC;
sp->type = T_ERROR_HANDLER;
sp->u.error_handler = pop_sprintf_state;
last = 0;
for (fpos = 0; 1; fpos++) {
char c = format_str[fpos];
if (c == '\n' || !c) {
int column_stat = 0;
if (last != fpos) {
add_nstr(format_str + last, fpos - last);
last = fpos + 1;
} else last++;
if (!sprintf_state->csts) {
if (!c)
break;
ADD_CHAR('\n');
continue;
}
ADD_CHAR('\n');
while (sprintf_state->csts) {
cst **temp;
temp = &(sprintf_state->csts);
while (*temp) {
if ((*temp)->info & INFO_COLS) {
if (*((*temp)->d.col - 1) != '\n')
while (*((*temp)->d.col) == ' ')
(*temp)->d.col++;
add_pad(0, (*temp)->start - get_curpos());
column_stat = add_column(temp, 0);
if (!column_stat)
temp = &((*temp)->next);
} else {
add_pad(0, (*temp)->start - get_curpos());
if (!add_table(temp))
temp = &((*temp)->next);
}
} /* of while (*temp) */
if (sprintf_state->csts || c == '\n')
ADD_CHAR('\n');
} /* of while (sprintf_state->csts) */
if (column_stat == 2)
ADD_CHAR('\n');
if (!c)
break;
} else
if (c == '%') {
if (last != fpos) {
add_nstr(format_str + last, fpos - last);
last = fpos + 1;
} else last++;
if (format_str[fpos + 1] == '%') {
ADD_CHAR('%');
fpos++;
last++;
continue;
}
GET_NEXT_ARG;
fs = 0;
pres = 0;
pad.len = 0;
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';
}
if (pres < 0) pres = 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.what = "0";
pad.len = 1;
} else {
if (format_str[fpos] == '*') {
if (carg->type != T_NUMBER)
ERROR(ERR_INVALID_STAR);
fs = carg->u.number;
if (fs < 0) fs = 0;
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 (fs < 0) fs = 0;
if (pres == -2) { /* colon */
pres = fs;
}
}
fpos--; /* about 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;
#ifdef DEBUG
case '%':
finfo |= INFO_T_NULL;
break; /* never reached */
#endif
case 'O':
finfo |= INFO_T_LPC;
break;
case 's':
finfo |= INFO_T_STRING;
break;
case 'd':
case 'i':
finfo |= INFO_T_INT;
break;
case 'f':
finfo |= INFO_T_FLOAT;
break;
case 'c':
finfo |= INFO_T_CHAR;
break;
case 'o':
finfo |= INFO_T_OCT;
break;
case 'x':
finfo |= INFO_T_HEX;
break;
case 'X':
finfo |= INFO_T_C_HEX;
break;
case '\'':
fpos++;
pad.what = format_str + fpos;
while (1) {
if (!format_str[fpos])
ERROR(ERR_UNEXPECTED_EOS);
if (format_str[fpos] == '\\') {
if (!format_str[++fpos])
ERROR(ERR_UNEXPECTED_EOS);
} else
if (format_str[fpos] == '\'') {
pad.len = format_str + fpos - pad.what;
if (!pad.len)
ERROR(ERR_NULL_PS);
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_ARRAY)
ERROR(ERR_ARRAY_EXPECTED);
if (carg->u.arr->size == 0) {
last = fpos;
fpos--; /* 'bout to get incremented */
continue;
}
carg = (argv + sprintf_state->cur_arg)->u.arr->item;
nelemno = 1; /* next element number */
}
while (1) {
if ((finfo & INFO_T) == INFO_T_LPC) {
outbuffer_t outbuf;
outbuf_zero(&outbuf);
svalue_to_string(carg, &outbuf, 0, 0, 0);
outbuf_fix(&outbuf);
sprintf_state->clean.type = T_STRING;
sprintf_state->clean.subtype = STRING_MALLOC;
sprintf_state->clean.u.string = outbuf.buffer;
carg = &(sprintf_state->clean);
finfo ^= INFO_T_LPC;
finfo |= INFO_T_STRING;
}
if ((finfo & INFO_T) == INFO_T_ERROR) {
ERROR(ERR_INVALID_FORMAT_STR);
#ifdef DEBUG
} else if ((finfo & INFO_T) == INFO_T_NULL) {
/* never reached... */
fprintf(stderr, "/%s: (s)printf: INFO_T_NULL.... found.\n",
current_object->obname);
ADD_CHAR('%');
#endif
} else if ((finfo & INFO_T) == INFO_T_STRING) {
int slen;
/*
* %s null handling added 930709 by Luke Mewburn
* <zak@rmit.oz.au>
*/
if (carg->type == T_NUMBER && carg->u.number == 0) {
sprintf_state->clean.type = T_STRING;
sprintf_state->clean.subtype = STRING_MALLOC;
sprintf_state->clean.u.string = string_copy(NULL_MSG, "sprintf NULL");
carg = &(sprintf_state->clean);
} else
if (carg->type != T_STRING) {
ERROR(ERR_INCORRECT_ARG_S);
}
slen = SVALUE_STRLEN(carg);
if ((finfo & INFO_COLS) || (finfo & INFO_TABLE)) {
cst **temp;
if (!fs) {
ERROR(ERR_CST_REQUIRES_FS);
}
temp = &(sprintf_state->csts);
while (*temp)
temp = &((*temp)->next);
if (finfo & INFO_COLS) {
int tmp;
if (pres > fs) pres = fs;
*temp = ALLOCATE(cst, TAG_TEMPORARY, "string_print: 3");
(*temp)->next = 0;
(*temp)->d.col = carg->u.string;
(*temp)->pad = make_pad(&pad);
(*temp)->size = fs;
(*temp)->pres = (pres) ? pres : fs;
(*temp)->info = finfo;
(*temp)->start = get_curpos();
#ifdef TCC
puts("tcc has some bugs");
#endif
tmp = ((format_str[fpos] != '\n')
&& (format_str[fpos] != '\0'))
|| ((finfo & INFO_ARRAY)
&& (nelemno < (argv + sprintf_state->cur_arg)->u.arr->size));
tmp = add_column(temp, tmp);
if (tmp == 2 && !format_str[fpos]) {
ADD_CHAR('\n');
}
} else {/* (finfo & INFO_TABLE) */
unsigned int n, len, max_len;
const char *p1, *p2;
#define TABLE carg->u.string
(*temp) = ALLOCATE(cst, TAG_TEMPORARY, "string_print: 4");
(*temp)->d.tab = 0;
(*temp)->pad = make_pad(&pad);
(*temp)->info = finfo;
(*temp)->start = get_curpos();
(*temp)->next = 0;
max_len = 0;
n = 1;
p2 = p1 = TABLE;
while (*p1) {
if (*p1 == '\n') {
if (p1 - p2 > max_len)
max_len = p1 - p2;
p1++;
if (*(p2 = p1))
n++;
} else
p1++;
}
if (!pres) {
/* the null terminated word */
if (p1 - p2 > max_len)
max_len = p1 - p2;
pres = fs / (max_len + 2); /* at least two
* separating spaces */
if (!pres)
pres = 1;
/* This moves some entries from the right side
* of the table to fill out the last line,
* which makes the table look a bit nicer.
* E.g.
* (n=13,p=6) (l=3,p=5)
* X X X X X X X X X X X
* X X X X X X -> X X X X X
* X X X X X
*
*/
len = (n-1)/pres + 1;
if (n > pres && n % pres)
pres -= (pres - n % pres) / len;
} else {
len = (n-1)/pres + 1;
}
(*temp)->size = fs / pres;
(*temp)->remainder = fs % pres;
if (n < pres) {
/* If we have fewer elements than columns,
* pretend we are dealing with a smaller
* table.
*/
(*temp)->remainder += (pres - n)*((*temp)->size);
pres = n;
}
(*temp)->d.tab = CALLOCATE(pres + 1, tab_data_t,
TAG_TEMPORARY, "string_print: 5");
(*temp)->nocols = pres; /* heavy sigh */
(*temp)->d.tab[0].start = TABLE;
if (pres == 1) {
(*temp)->d.tab[1].start = TABLE + SVALUE_STRLEN(carg) + 1;
} else {
i = 1; /* the next column number */
n = 0; /* the current "word" number in this
* column */
p1 = TABLE;
while (*p1) {
if (*p1++ == '\n' && ++n >= len) {
(*temp)->d.tab[i++].start = p1;
n = 0;
}
}
for ( ; i <= pres; i++)
(*temp)->d.tab[i].start = ++p1;
}
for (i = 0; i < pres; i++)
(*temp)->d.tab[i].cur = (*temp)->d.tab[i].start;
add_table(temp);
}
} else { /* not column or table */
const char *tmp = carg->u.string; //work around tcc bug;
#ifdef USE_ICONV
int width = 0;
int i;
if(pres){
for(i=0; i<slen && width != pres; i++)
if(tmp[i] & 0x80){
if(!(tmp[i+1] & 0x80))
width++;
} else
width++;
if(width == pres)
slen = i;
}
#else
if (pres && pres < slen)
slen = pres;
#endif
add_justified(tmp, slen, &pad, fs, finfo,
(((format_str[fpos] != '\n') && (format_str[fpos] != '\0'))
|| ((finfo & INFO_ARRAY) && (nelemno < (argv + sprintf_state->cur_arg)->u.arr->size)))
|| (slen && (carg->u.string[slen - 1] != '\n')));
}
} else if (finfo & INFO_T_INT) { /* one of the integer
* types */
char cheat[20];
char temp[100];
*cheat = '%';
i = 1;
switch (finfo & INFO_PP) {
case INFO_PP_SPACE:
cheat[i++] = ' ';
break;
case INFO_PP_PLUS:
cheat[i++] = '+';
break;
}
if (pres) {
cheat[i++] = '.';
if(pres >= sizeof(temp))
sprintf(cheat + i, "%ld", sizeof(temp) - 1);
else
sprintf(cheat + i, "%d", pres);
i += strlen(cheat + i);
}
switch (finfo & INFO_T) {
case INFO_T_INT:
cheat[i++] = 'l';
cheat[i++] = 'd';
break;
case INFO_T_FLOAT:
cheat[i++] = 'f';
break;
case INFO_T_CHAR:
cheat[i++] = 'c';
break;
case INFO_T_OCT:
cheat[i++] = 'l';
cheat[i++] = 'o';
break;
case INFO_T_HEX:
cheat[i++] = 'l';
cheat[i++] = 'x';
break;
case INFO_T_C_HEX:
cheat[i++] = 'l';
cheat[i++] = 'X';
break;
default:
ERROR(ERR_BAD_INT_TYPE);
}
if ((cheat[i - 1] == 'f' && carg->type != T_REAL) || (cheat[i - 1] != 'f' && carg->type != T_NUMBER)) {
#ifdef RETURN_ERROR_MESSAGES
sprintf(buff,
"ERROR: (s)printf(): Incorrect argument type to %%%c. (arg: %u)\n",
cheat[i - 1], sprintf_state->cur_arg);
fprintf(stderr, "Program /%s File: %s: %s", current_prog->name,
get_line_number_if_any(), buff);
debug_message("%s", buff);
if (current_object) {
debug_message("program: /%s, object: %s, file: %s\n",
current_prog ? current_prog->name : "",
current_object->name,
get_line_number_if_any());
}
ERROR(ERR_RECOVERY_ONLY);
#else
error("ERROR: (s)printf(): Incorrect argument type to %%%c.\n",
cheat[i - 1]);
#endif /* RETURN_ERROR_MESSAGES */
}
cheat[i] = '\0';
if (carg->type == T_REAL) {
sprintf(temp, cheat, carg->u.real);
} else
sprintf(temp, cheat, carg->u.number);
{
int tmpl = strlen(temp);
add_justified(temp, tmpl, &pad, fs, finfo,
(((format_str[fpos] != '\n') && (format_str[fpos] != '\0'))
|| ((finfo & INFO_ARRAY) && (nelemno < (argv + sprintf_state->cur_arg)->u.arr->size))));
}
} else /* type not found */
ERROR(ERR_UNDEFINED_TYPE);
if (sprintf_state->clean.type != T_NUMBER) {
free_svalue(&(sprintf_state->clean), "string_print_formatted");
sprintf_state->clean.type = T_NUMBER;
}
if (!(finfo & INFO_ARRAY))
break;
if (nelemno >= (argv + sprintf_state->cur_arg)->u.arr->size)
break;
carg = (argv + sprintf_state->cur_arg)->u.arr->item + nelemno++;
} /* end of while (1) */
last = fpos;
fpos--; /* bout to get incremented */
}
} /* end of for (fpos=0; 1; fpos++) */
outbuf_fix(&sprintf_state->obuff);
retvalue = sprintf_state->obuff.buffer;
sprintf_state->obuff.buffer = 0;
pop_stack(); /* pop off our error handler, will call pop_sprintf_state */
return retvalue;
} /* end of string_print_formatted() */
#endif /* defined(F_SPRINTF) || defined(F_PRINTF) */