This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/packages/PLStream/pl-fmt.c
2011-03-02 09:18:51 +00:00

1067 lines
23 KiB
C

/* $Id$
Part of SWI-Prolog
Author: Jan Wielemaker
E-mail: wielemak@science.uva.nl
WWW: http://www.swi-prolog.org
Copyright (C): 1985-2007, University of Amsterdam
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Formatted output (Prolog predicates format/[1,2,3]). One day, the C
source should also use format() to produce error messages, etc.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "pl-incl.h"
#include "pl-ctype.h"
#include "pl-utf8.h"
#include <ctype.h>
static char * formatNumber(bool split, int div, int radix,
bool small, Number n, Buffer out);
static char * formatFloat(int how, int arg, Number f, Buffer out);
#define MAXRUBBER 100
struct rubber
{ size_t where; /* where is rubber in output */
size_t size; /* how big should it be */
pl_wchar_t pad; /* padding character */
};
typedef struct
{ IOSTREAM *out; /* our output stream */
int column; /* current column */
tmp_buffer buffer; /* bin for characters with tabs */
size_t buffered; /* characters in buffer */
int pending_rubber; /* number of not-filled ~t's */
struct rubber rub[MAXRUBBER];
} format_state;
#define BUFSIZE 1024
#define DEFAULT (-1)
#define SHIFT { argc--; argv++; }
#define NEED_ARG { if ( argc <= 0 ) \
{ FMT_ERROR("not enough arguments"); \
} \
}
#define FMT_ERROR(fmt) return (void)Sunlock(fd), \
PL_error(NULL, 0, NULL, ERR_FORMAT, fmt)
#define FMT_ARG(c, a) return (void)Sunlock(fd), \
PL_error(NULL, 0, NULL, \
ERR_FORMAT_ARG, c, a)
static int
update_column(int col, int c)
{ switch(c)
{ case '\n': return 0;
case '\r': return 0;
case '\t': return (col + 1) | 0x7;
case '\b': return (col <= 0 ? 0 : col - 1);
default: return col + 1;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Low-level output. If there is pending rubber the output is stored in
UTF-8 format in the state's `buffer'. The `buffered' field represents
the number of UTF-8 characters in the buffer.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int
outchr(format_state *state, int chr)
{ if ( state->pending_rubber )
{ if ( chr > 0x7f )
{ char buf[8];
char *s, *e;
e = utf8_put_char(buf, chr);
for(s=buf; s<e; s++)
addBuffer((Buffer)&state->buffer, *s, char);
} else
{ char c = chr;
addBuffer((Buffer)&state->buffer, c, char);
}
state->buffered++;
} else
{ if ( Sputcode(chr, state->out) < 0 )
return FALSE;
}
state->column = update_column(state->column, chr);
return TRUE;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Emit ASCII 0-terminated strings resulting from sprintf() on numeric
arguments. No fuzz with wide characters here.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int
outstring(format_state *state, const char *s, size_t len)
{ const char *q;
const char *e = &s[len];
if ( state->pending_rubber )
{ addMultipleBuffer(&state->buffer, s, len, char);
state->buffered += len;
} else
{ for(q=s; q < e; q++)
{ if ( Sputcode(*q&0xff, state->out) < 0 )
return FALSE;
}
}
for(q=s; q < e; q++)
state->column = update_column(state->column, *q&0xff);
return TRUE;
}
static int
outstring0(format_state *state, const char *s)
{ return outstring(state, s, strlen(s));
}
static int
oututf8(format_state *state, const char *s, size_t len)
{ const char *e = &s[len];
while(s<e)
{ int chr;
s = utf8_get_char(s, &chr);
if ( !outchr(state, chr) )
return FALSE;
}
return TRUE;
}
static int
outtext(format_state *state, PL_chars_t *txt)
{ switch(txt->encoding)
{ case ENC_ISO_LATIN_1:
return outstring(state, txt->text.t, txt->length);
case ENC_WCHAR:
{ const pl_wchar_t *s = txt->text.w;
const pl_wchar_t *e = &s[txt->length];
while(s<e)
{ if ( !outchr(state, *s++) )
return FALSE;
}
return TRUE;
}
default:
{ assert(0);
return FALSE;
}
}
}
#define format_predicates (GD->format.predicates)
static int update_column(int, Char);
static bool do_format(IOSTREAM *fd, PL_chars_t *fmt, int ac, term_t av);
static void distribute_rubber(struct rubber *, int, int);
static int emit_rubber(format_state *state);
/********************************
* PROLOG CONNECTION *
********************************/
word
pl_format_predicate(term_t chr, term_t descr)
{ int c;
predicate_t proc = NULL;
Symbol s;
int arity;
if ( !PL_get_char_ex(chr, &c, FALSE) )
fail;
if ( !get_procedure(descr, &proc, 0, GP_CREATE) )
fail;
PL_predicate_info(proc, NULL, &arity, NULL);
if ( arity == 0 )
return PL_error(NULL, 0, "arity must be > 0", ERR_DOMAIN,
PL_new_atom("format_predicate"),
descr);
if ( !format_predicates )
format_predicates = newHTable(8);
if ( (s = lookupHTable(format_predicates, (void *)(intptr_t)c)) )
s->value = proc;
else
addHTable(format_predicates, (void *)(intptr_t)c, proc);
succeed;
}
word
pl_current_format_predicate(term_t chr, term_t descr, control_t h)
{ GET_LD
Symbol s = NULL;
TableEnum e;
fid_t fid;
switch( ForeignControl(h) )
{ case FRG_FIRST_CALL:
if ( !format_predicates )
fail;
e = newTableEnum(format_predicates);
break;
case FRG_REDO:
e = ForeignContextPtr(h);
break;
case FRG_CUTTED:
e = ForeignContextPtr(h);
freeTableEnum(e);
default:
succeed;
}
if ( !(fid = PL_open_foreign_frame()) )
{ freeTableEnum(e);
return FALSE;
}
while( (s=advanceTableEnum(e)) )
{ if ( PL_unify_integer(chr, (intptr_t)s->name) &&
PL_unify_predicate(descr, (predicate_t)s->value, 0) )
{ PL_close_foreign_frame(fid);
ForeignRedoPtr(e);
}
PL_rewind_foreign_frame(fid);
}
PL_close_foreign_frame(fid);
freeTableEnum(e);
fail;
}
static word
format_impl(IOSTREAM *out, term_t format, term_t Args)
{ GET_LD
term_t argv;
int argc = 0;
term_t args = PL_copy_term_ref(Args);
int rval;
PL_chars_t fmt;
if ( !PL_get_text(format, &fmt, CVT_ALL|BUF_RING) )
return PL_error("format", 3, NULL, ERR_TYPE, ATOM_text, format);
if ( (argc = (int)lengthList(args, FALSE)) >= 0 )
{ term_t head = PL_new_term_ref();
int n = 0;
argv = PL_new_term_refs(argc);
while( PL_get_list(args, head, args) )
PL_put_term(argv+n++, head);
} else
{ argc = 1;
argv = PL_new_term_refs(argc);
PL_put_term(argv, args);
}
startCritical;
switch(fmt.storage) /* format can do call-back! */
{ case PL_CHARS_RING:
case PL_CHARS_STACK:
PL_save_text(&fmt, BUF_MALLOC);
break;
default:
break;
}
rval = do_format(out, &fmt, argc, argv);
PL_free_text(&fmt);
if ( !endCritical )
return FALSE;
return rval;
}
word
pl_format3(term_t out, term_t format, term_t args)
{ redir_context ctx;
word rc;
#if __YAP_PROLOG__
/*
YAP allows the last argument to format to be of the form
module:[]
*/
YAP_Term mod;
#endif
if ( (rc=setupOutputRedirect(out, &ctx, FALSE)) ) {
#if __YAP_PROLOG__
/* module processing */
{
args = Yap_fetch_module_for_format(args, &mod);
}
#endif
{ if ( (rc = format_impl(ctx.stream, format, args)) )
rc = closeOutputRedirect(&ctx);
else
discardOutputRedirect(&ctx);
}
#if __YAP_PROLOG__
YAP_SetCurrentModule(mod);
#endif
}
return rc;
}
word
pl_format(term_t fmt, term_t args)
{ return pl_format3(0, fmt, args);
}
static inline int
get_chr_from_text(const PL_chars_t *t, int index)
{ switch(t->encoding)
{ case ENC_ISO_LATIN_1:
return t->text.t[index]&0xff;
case ENC_WCHAR:
return t->text.w[index];
default:
assert(0);
return 0; /* not reached */
}
}
/********************************
* ACTUAL FORMATTING *
********************************/
static bool
do_format(IOSTREAM *fd, PL_chars_t *fmt, int argc, term_t argv)
{ GET_LD
format_state state; /* complete state */
int tab_stop = 0; /* padded tab stop */
Symbol s;
unsigned int here = 0;
int rc = TRUE;
Slock(fd); /* buffer locally */
state.out = fd;
state.pending_rubber = 0;
initBuffer(&state.buffer);
state.buffered = 0;
if ( fd->position )
state.column = fd->position->linepos;
else
state.column = 0;
while(here < fmt->length)
{ int c = get_chr_from_text(fmt, here);
switch(c)
{ case '~':
{ int arg = DEFAULT; /* Numeric argument */
/* Get the numeric argument */
c = get_chr_from_text(fmt, ++here);
if ( isDigitW(c) )
{ arg = c - '0';
here++;
while(here < fmt->length)
{ c = get_chr_from_text(fmt, here);
if ( isDigitW(c) )
{ int dw = c - '0';
int arg2 = arg*10 + dw;
if ( (arg2 - dw)/10 != arg ) /* see mul64() in pl-arith.c */
{ FMT_ERROR("argument overflow");
}
arg = arg2;
here++;
} else
break;
}
} else if ( c == '*' )
{ NEED_ARG;
if ( PL_get_integer(argv, &arg) )
{ SHIFT;
} else
FMT_ERROR("no or negative integer for `*' argument");
c = get_chr_from_text(fmt, ++here);
} else if ( c == '`' && here < fmt->length )
{ arg = get_chr_from_text(fmt, ++here);
c = get_chr_from_text(fmt, ++here);
}
/* Check for user defined format */
if ( format_predicates &&
(s = lookupHTable(format_predicates, (void*)((intptr_t)c))) )
{ predicate_t proc = (predicate_t) s->value;
int arity;
term_t av;
char buf[BUFSIZE];
char *str = buf;
size_t bufsize = BUFSIZE;
unsigned int i;
PL_predicate_info(proc, NULL, &arity, NULL);
av = PL_new_term_refs(arity);
if ( arg == DEFAULT )
PL_put_atom(av+0, ATOM_default);
else
PL_put_integer(av+0, arg);
for(i=1; i < arity; i++)
{ NEED_ARG;
PL_put_term(av+i, argv);
SHIFT;
}
tellString(&str, &bufsize, ENC_UTF8);
rc = PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, proc, av);
toldString();
if ( !rc )
{ if ( str != buf )
free(str);
goto out;
}
oututf8(&state, str, bufsize);
if ( str != buf )
free(str);
here++;
} else
{ switch(c) /* Build in formatting */
{ case 'a': /* atomic */
{ PL_chars_t txt;
NEED_ARG;
if ( !PL_get_text(argv, &txt, CVT_ATOMIC) )
FMT_ARG("a", argv);
SHIFT;
outtext(&state, &txt);
here++;
break;
}
case 'c': /* ~c: character code */
{ int chr;
NEED_ARG;
if ( PL_get_integer(argv, &chr) && chr >= 0 )
{ int times = (arg == DEFAULT ? 1 : arg);
SHIFT;
while(times-- > 0)
{ outchr(&state, chr);
}
} else
FMT_ARG("c", argv);
here++;
break;
}
case 'e': /* exponential float */
case 'E': /* Exponential float */
case 'f': /* float */
case 'g': /* shortest of 'f' and 'e' */
case 'G': /* shortest of 'f' and 'E' */
{ number n;
union {
tmp_buffer b;
buffer b1;
} u;
NEED_ARG;
if ( !valueExpression(argv, &n PASS_LD) )
{ char f[2];
f[0] = c;
f[1] = EOS;
FMT_ARG(f, argv);
}
SHIFT;
initBuffer(&u.b);
formatFloat(c, arg, &n, &u.b1);
clearNumber(&n);
outstring0(&state, baseBuffer(&u.b, char));
discardBuffer(&u.b);
here++;
break;
}
case 'd': /* integer */
case 'D': /* grouped integer */
case 'r': /* radix number */
case 'R': /* Radix number */
{ number i;
tmp_buffer b;
NEED_ARG;
if ( !valueExpression(argv, &i PASS_LD) ||
!toIntegerNumber(&i, 0) )
{ char f[2];
f[0] = c;
f[1] = EOS;
FMT_ARG(f, argv);
}
SHIFT;
if ( arg == DEFAULT )
arg = 0;
initBuffer(&b);
if ( c == 'd' || c == 'D' )
{ formatNumber(c == 'D', arg, 10, TRUE, &i, (Buffer)&b);
} else
{ if ( arg < 1 || arg > 36 )
{ term_t r = PL_new_term_ref();
PL_put_integer(r, arg);
Sunlock(fd);
return PL_error(NULL, 0, NULL, ERR_DOMAIN,
ATOM_radix, r);
}
formatNumber(FALSE, 0, arg, c == 'r', &i, (Buffer)&b);
}
clearNumber(&i);
outstring0(&state, baseBuffer(&b, char));
discardBuffer(&b);
here++;
break;
}
case 's': /* string */
{ PL_chars_t txt;
NEED_ARG;
if ( !PL_get_text(argv, &txt, CVT_LIST|CVT_STRING) &&
!PL_get_text(argv, &txt, CVT_ATOM) ) /* SICStus compat */
FMT_ARG("s", argv);
outtext(&state, &txt);
SHIFT;
here++;
break;
}
case 'i': /* ignore */
{ NEED_ARG;
SHIFT;
here++;
break;
}
{ Func f;
char buf[BUFSIZE];
char *str;
case 'k': /* write_canonical */
f = pl_write_canonical;
goto pl_common;
case 'p': /* print */
f = pl_print;
goto pl_common;
case 'q': /* writeq */
f = pl_writeq;
goto pl_common;
case 'w': /* write */
f = pl_write;
pl_common:
NEED_ARG;
if ( state.pending_rubber )
{ size_t bufsize = BUFSIZE;
str = buf;
tellString(&str, &bufsize, ENC_UTF8);
(*f)(argv);
toldString();
oututf8(&state, str, bufsize);
if ( str != buf )
free(str);
} else
{ if ( fd->position &&
fd->position->linepos == state.column )
{ IOSTREAM *old = Scurout;
Scurout = fd;
rc = (int)(*f)(argv);
Scurout = old;
if ( !rc )
goto out;
state.column = fd->position->linepos;
} else
{ size_t bufsize = BUFSIZE;
str = buf;
tellString(&str, &bufsize, ENC_UTF8);
(*f)(argv);
toldString();
oututf8(&state, str, bufsize);
if ( str != buf )
free(str);
}
}
SHIFT;
here++;
break;
}
case 'W': /* write_term(Value, Options) */
{ char buf[BUFSIZE];
char *str;
if ( argc < 2 )
{ FMT_ERROR("not enough arguments");
}
if ( state.pending_rubber )
{ size_t bufsize = BUFSIZE;
str = buf;
tellString(&str, &bufsize, ENC_UTF8);
rc = (int)pl_write_term(argv, argv+1);
toldString();
if ( !rc )
goto out;
oututf8(&state, str, bufsize);
if ( str != buf )
free(str);
} else
{ if ( fd->position &&
fd->position->linepos == state.column )
{ IOSTREAM *old = Scurout;
Scurout = fd;
rc = (int)pl_write_term(argv, argv+1);
Scurout = old;
if ( !rc )
goto out;
state.column = fd->position->linepos;
} else
{ size_t bufsize = BUFSIZE;
str = buf;
tellString(&str, &bufsize, ENC_UTF8);
rc = (int)pl_write_term(argv, argv+1);
if ( !rc )
goto out;
toldString();
oututf8(&state, str, bufsize);
if ( str != buf )
free(str);
}
}
SHIFT;
SHIFT;
here++;
break;
}
case '@':
{ char buf[BUFSIZE];
char *str = buf;
size_t bufsize = BUFSIZE;
term_t ex = 0;
int rval;
if ( argc < 1 )
{ FMT_ERROR("not enough arguments");
}
tellString(&str, &bufsize, ENC_UTF8);
rval = callProlog(NULL, argv, PL_Q_CATCH_EXCEPTION, &ex);
toldString();
oututf8(&state, str, bufsize);
if ( str != buf )
free(str);
if ( !rval )
{ Sunlock(fd);
if ( ex )
return PL_raise_exception(ex);
else
fail;
}
SHIFT;
here++;
break;
}
case '~': /* ~ */
{ outchr(&state, '~');
here++;
break;
}
case 'n': /* \n */
case 'N': /* \n if not on newline */
{ if ( arg == DEFAULT )
arg = 1;
if ( c == 'N' && state.column == 0 )
arg--;
while( arg-- > 0 )
outchr(&state, '\n');
here++;
break;
}
case 't': /* insert tab */
{ if ( state.pending_rubber >= MAXRUBBER )
FMT_ERROR("Too many tab stops");
state.rub[state.pending_rubber].where = state.buffered;
state.rub[state.pending_rubber].pad =
(arg == DEFAULT ? (pl_wchar_t)' '
: (pl_wchar_t)arg);
state.rub[state.pending_rubber].size = 0;
state.pending_rubber++;
here++;
break;
}
case '|': /* set tab */
{ int stop;
if ( arg == DEFAULT )
arg = state.column;
case '+': /* tab relative */
if ( arg == DEFAULT )
arg = 8;
stop = (c == '+' ? tab_stop + arg : arg);
if ( state.pending_rubber == 0 ) /* nothing to distribute */
{ state.rub[0].where = state.buffered;
state.rub[0].pad = ' ';
state.pending_rubber++;
}
distribute_rubber(state.rub,
state.pending_rubber,
stop - state.column);
emit_rubber(&state);
state.column = tab_stop = stop;
here++;
break;
}
default:
{ term_t ex = PL_new_term_ref();
Sunlock(fd);
PL_put_atom(ex, codeToAtom(c));
return PL_error("format", 2, NULL, ERR_EXISTENCE,
PL_new_atom("format_character"),
ex);
}
}
}
break; /* the '~' switch */
}
default:
{ outchr(&state, c);
here++;
break;
}
}
}
if ( state.pending_rubber ) /* not closed ~t: flush out */
emit_rubber(&state);
out:
Sunlock(fd);
return rc;
}
static void
distribute_rubber(struct rubber *r, int rn, int space)
{ if ( space > 0 )
{ int s = space / rn;
int n, m;
for(n=0; n < rn; n++) /* give them equal size */
r[n].size = s;
/* distribute from the center */
space -= s*rn;
for(m = rn / 2, n = 0; space; n++, space--)
{ r[m + (n % 2 ? n : -n)].size++;
}
} else
{ int n;
for(n=0; n < rn; n++) /* set all rubber to 0 */
r[n].size = 0;
}
}
static int
emit_rubber(format_state *state)
{ const char *s = baseBuffer(&state->buffer, char);
const char *e = &s[entriesBuffer(&state->buffer, char)];
struct rubber *r = state->rub;
int rn = state->pending_rubber;
size_t j;
for(j = 0; s <= e; j++)
{ int chr;
if ( rn && r->where == j )
{ size_t n;
for(n=0; n<r->size; n++)
{ if ( Sputcode(r->pad, state->out) < 0 )
return FALSE;
}
r++;
rn--;
}
if ( s < e )
{ s = utf8_get_char(s, &chr);
if ( Sputcode(chr, state->out) < 0 )
return FALSE;
} else
break;
}
discardBuffer(&state->buffer);
initBuffer(&state->buffer);
state->buffered = 0;
state->pending_rubber = 0;
return TRUE;
}
/* format an integer according to a number of modifiers at various
radius. `split' is a boolean asking to put ',' between each group
of three digits (e.g. 67,567,288). `div' askes to divide the number
by radix^`div' before printing. `radix' is the radix used for
conversion. `n' is the number to be converted.
** Fri Aug 19 22:26:41 1988 jan@swivax.UUCP (Jan Wielemaker) */
static char *
formatNumber(bool split, int div, int radix, bool small, Number i,
Buffer out)
{ switch(i->type)
{ case V_INTEGER:
{ int64_t n = i->value.i;
char buf[100];
char *tmp, *end, *s;
int before = (div == 0);
int digits = 0;
bool negative = FALSE;
if ( div+3 > (int)sizeof(buf) ) /* 0.000NNNN with div digits after 0. */
{ tmp = PL_malloc(div+3);
end = tmp+div+3;
} else
{ tmp = buf;
end = tmp+sizeof(buf);
}
s = end; /* i.e. start at the end */
*--s = EOS;
if ( n < 0 )
{ n = -n;
negative = TRUE;
}
if ( n == 0 && div == 0 )
{ *--s = '0';
} else
{ while( n > 0 || div >= 0 )
{ if ( div-- == 0 && !before )
{ *--s = '.';
before = 1;
}
if ( split && before && (digits++ % 3) == 0 && digits != 1 )
*--s = ',';
*--s = digitName((int)(n % radix), small);
n /= radix;
}
if ( negative )
*--s = '-';
}
addMultipleBuffer(out, s, end-s, char);
if ( tmp != buf )
PL_free(tmp);
return baseBuffer(out, char);
}
#ifdef O_GMP
case V_MPZ:
{ size_t len = mpz_sizeinbase(i->value.mpz, radix);
char tmp[256];
char *buf;
if ( len+2 > sizeof(tmp) )
buf = PL_malloc(len+2);
else
buf = tmp;
mpz_get_str(buf, radix, i->value.mpz);
if ( !small && radix > 10 )
{ char *s;
for(s=buf; *s; s++)
*s = toupper(*s);
}
if ( split || div > 0 )
{ int before = (int)(len-div);
int leading;
char *s = buf;
if ( *s == '-' )
{ addBuffer(out, *s, char);
s++;
}
if ( split )
{ leading = before % 3;
if ( leading == 0 )
leading = 3;
} else
{ leading = (int)len;
}
for(; *s; s++)
{ if ( before-- == 0 && div > 0 )
{ addBuffer(out, '.', char);
} else if ( leading-- == 0 && before > 0 )
{ addBuffer(out, ',', char);
leading = 2;
}
addBuffer(out, *s, char);
}
addBuffer(out, EOS, char);
} else
{ addMultipleBuffer(out, buf, strlen(buf), char);
addBuffer(out, EOS, char);
}
if ( buf != tmp )
PL_free(buf);
return baseBuffer(out, char);
}
#endif /*O_GMP*/
default:
assert(0);
return NULL;
}
}
static char *
formatFloat(int how, int arg, Number f, Buffer out)
{ if ( arg == DEFAULT )
arg = 6;
switch(f->type)
{
#ifdef O_GMP
mpf_t mpf;
case V_MPZ:
mpf_init2(mpf, arg*4);
mpf_set_z(mpf, f->value.mpz);
goto print;
case V_MPQ:
{ char tmp[12];
int size;
int written;
int fbits;
switch(how)
{ case 'f':
case 'g':
case 'G':
{ mpz_t iv;
mpz_init(iv);
mpz_set_q(iv, f->value.mpq);
fbits = (int)mpz_sizeinbase(iv, 2) + 4*arg;
mpz_clear(iv);
break;
}
default:
fbits = 4*arg;
}
mpf_init2(mpf, fbits);
mpf_set_q(mpf, f->value.mpq);
print:
Ssprintf(tmp, "%%.%dF%c", arg, how);
size = 0;
written = arg+4;
while(written >= size)
{ size = written+1;
growBuffer(out, size); /* reserve for -.e<null> */
written = gmp_snprintf(baseBuffer(out, char), size, tmp, mpf);
}
mpf_clear(mpf);
out->top = out->base + written;
return baseBuffer(out, char);
}
#endif
case V_INTEGER:
promoteToFloatNumber(f);
/*FALLTHROUGH*/
case V_FLOAT:
{ char tmp[12];
int written = arg+20;
int size = 0;
Ssprintf(tmp, "%%.%d%c", arg, how);
while(written >= size)
{ size = written+1;
growBuffer(out, size);
written = snprintf(baseBuffer(out, char), size, tmp, f->value.f);
}
out->top = out->base + written;
return baseBuffer(out, char);
}
}
return NULL;
}