588 lines
16 KiB
C
588 lines
16 KiB
C
#ifndef NO_POSIX_SOURCE
|
|
#undef _POSIX_SOURCE
|
|
#define _POSIX_SOURCE 1
|
|
#undef _POSIX_C_SOURCE
|
|
#define _POSIX_C_SOURCE 2
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <float.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "cell.h"
|
|
#include "default.h"
|
|
#include "eval.h"
|
|
#include "main.h"
|
|
#include "utf8.h"
|
|
|
|
const char *Adjust_Name[] =
|
|
{ [LEFT] = "Left", [RIGHT] = "Right", [CENTER] = "Center",
|
|
[AUTOADJUST] = "Auto"
|
|
};
|
|
|
|
const char Adjust_Char[] = "lrca";
|
|
|
|
const char *FloatFormat_Name[] =
|
|
{ [FLT_DECIMAL] = "Decimal", [FLT_SCIENTIFIC] = "Scientific",
|
|
[FLT_COMPACT] = "Compact", [FLT_HEXACT] = "Hexact"
|
|
};
|
|
|
|
const char FloatFormat_Char[] = "dsch";
|
|
|
|
const char *ColorAspect_Name[] =
|
|
{ [FOREGROUND] = "Foreground", [BACKGROUND] = "Background"
|
|
};
|
|
const ColorNum DefaultCN[] =
|
|
{ [FOREGROUND] = 0, [BACKGROUND] = 16, [NUM_COLOR_ASPECTS] = 255 };
|
|
|
|
const char *TokVariety_Name[] =
|
|
{ [BASE_CONT] = "Base Content", [ITER_CONT] = "Iterative Content",
|
|
[ATTR_REF] = "Attribute Reference", [CURR_VAL] = "Current Value",
|
|
[RES_VAL] = "Resultant Value", [CONTINGENT] = "Contingent Content"
|
|
};
|
|
|
|
/* initcellcontents - make a fresh cell into the "empty" one; don't worry
|
|
about freeing anything there, that will have been handled. */
|
|
void initcellcontents(Cell *fresh)
|
|
{
|
|
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
|
|
fresh->tok[tv].type = EMPTY;
|
|
fresh->label = NULL;
|
|
fresh->precision = -1;
|
|
fresh->fform = DEF_FLOATFORM;
|
|
fresh->adjust = AUTOADJUST;
|
|
for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a)
|
|
fresh->aspect[a] = DefaultCN[a];
|
|
fresh->updated = 0;
|
|
fresh->shadowed = 0;
|
|
fresh->locked = 0;
|
|
fresh->transparent = 0;
|
|
fresh->ignored = 0;
|
|
fresh->clock_t0 = 0;
|
|
fresh->clock_t1 = 0;
|
|
fresh->clock_t2 = 0;
|
|
fresh->bold = 0;
|
|
fresh->underline = 0;
|
|
}
|
|
|
|
/* getcont -- get contents */
|
|
Token gettok(const Cell *cell, TokVariety v)
|
|
{
|
|
Token emp;
|
|
emp.type = EMPTY;
|
|
if (cell == NULLCELL) return emp;
|
|
if (v == CONTINGENT)
|
|
v = (cell->clock_t0 && cell->tok[ITER_CONT].type != EMPTY)
|
|
? ITER_CONT : BASE_CONT;
|
|
return cell->tok[v];
|
|
}
|
|
|
|
/* getadjust -- get cell adjustment */
|
|
Adjust getadjust(const Cell* cell)
|
|
{
|
|
if (cell == NULLCELL) return LEFT;
|
|
else if (cell->adjust == AUTOADJUST)
|
|
return (cell->tok[CURR_VAL].type == INT
|
|
|| cell->tok[CURR_VAL].type == FLOAT) ? RIGHT : LEFT;
|
|
else return cell->adjust;
|
|
}
|
|
|
|
/* shadowed -- is cell shadowed? */
|
|
bool shadowed(const Cell *cell)
|
|
{
|
|
return (cell != NULLCELL) && cell->shadowed;
|
|
}
|
|
|
|
/* isbold -- is cell bold? */
|
|
bool isbold(const Cell* cell)
|
|
{
|
|
return (cell != NULLCELL) && cell->bold;
|
|
}
|
|
|
|
/* getcolor -- a color associated to cell */
|
|
ColorNum getcolor(const Cell* cell, ColorAspect aspect)
|
|
{
|
|
return cell == NULLCELL ? DefaultCN[aspect] : cell->aspect[aspect];
|
|
}
|
|
|
|
/* isunderline -- is cell underlined? */
|
|
bool underlined(const Cell *cell)
|
|
{
|
|
return (cell != NULLCELL) && cell->underline;
|
|
}
|
|
|
|
/* locked -- is cell locked? */
|
|
bool locked(const Cell *cell)
|
|
{
|
|
return (cell != NULLCELL) && cell->locked;
|
|
}
|
|
|
|
/* transparent -- is cell transparent? */
|
|
bool transparent(const Cell* cell)
|
|
{
|
|
return (cell != NULLCELL) && cell->transparent;
|
|
}
|
|
|
|
/* ignored -- is cell ignored? */
|
|
bool ignored(const Cell *cell)
|
|
{
|
|
return (cell != NULLCELL) && cell->ignored;
|
|
}
|
|
|
|
|
|
/* getfltformat -- float format for numbers */
|
|
FloatFormat getfltformat(const Cell *cell )
|
|
{
|
|
return (cell == NULLCELL) ? DEF_FLOATFORM : cell->fform;
|
|
}
|
|
|
|
/* getprecision -- get cell precision */
|
|
int getprecision(const Cell *cell)
|
|
{
|
|
if (cell == NULLCELL || cell->precision == -1) return def_precision;
|
|
return cell->precision;
|
|
}
|
|
|
|
/* getlabel -- get cell label */
|
|
const char *getlabel(const Cell* cell)
|
|
{
|
|
if (cell == NULLCELL || cell->label == (char*)0) return "";
|
|
return cell->label;
|
|
}
|
|
|
|
/* copytokens -- copy a sequence of tokens, possibly reallocating dest */
|
|
static void copytokens(Token*** totoks, Token** fromtoks)
|
|
{
|
|
size_t from_len = tveclen(fromtoks);
|
|
if (from_len == 0)
|
|
{
|
|
tvecfree(*totoks);
|
|
*totoks = EMPTY_TVEC;
|
|
return;
|
|
}
|
|
|
|
size_t to_len = tveclen(*totoks);
|
|
if (from_len > to_len || *totoks == fromtoks)
|
|
{
|
|
if (*totoks != fromtoks) tvecfree(*totoks);
|
|
*totoks = malloc((from_len+1)*sizeof(Token*));
|
|
(*totoks)[from_len] = NULLTOKEN;
|
|
} else {
|
|
tvecfreetoks(*totoks);
|
|
}
|
|
for (size_t i = 0; i<from_len; ++i) /* destination already has NULLTOKEN at end */
|
|
{
|
|
if (fromtoks[i] == NULLTOKEN) (*totoks)[i] = NULLTOKEN;
|
|
else
|
|
{
|
|
(*totoks)[i] = malloc(sizeof(Token));
|
|
*((*totoks)[i]) = tcopy(*(fromtoks[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* freecellcontents -- free the resources of the cell at destruction time */
|
|
void freecellcontents(Cell *faded)
|
|
{
|
|
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
|
|
tfree(&(faded->tok[tv]));
|
|
if (faded->label != NULL) free(faded->label);
|
|
}
|
|
|
|
/* copycell - copies one Cell to another, handling any allocation issues */
|
|
void copycell(Cell *to, const Cell *fromcell, LabelHandling lh)
|
|
{
|
|
assert(to != NULLCELL);
|
|
if (to == fromcell) return;
|
|
|
|
freecellcontents(to);
|
|
if (fromcell != NULLCELL) {
|
|
memcpy(to, fromcell, sizeof(Cell));
|
|
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
|
|
if (tv <= MAX_PERSIST_TV) to->tok[tv] = tcopy(fromcell->tok[tv]);
|
|
else to->tok[tv].type = EMPTY;
|
|
if (lh != PRESERVE_LABEL && fromcell->label != (char*)0) {
|
|
size_t len = strlen(fromcell->label);
|
|
to->label = strcpy(malloc(len+2), fromcell->label);
|
|
(to->label)[len] = '_';
|
|
(to->label)[len+1] = '\0';
|
|
}
|
|
} else {
|
|
initcellcontents(to);
|
|
}
|
|
}
|
|
|
|
/* print_fident -- print a field identifier to a dest and return the number
|
|
of characters written
|
|
*/ /*{{{*/
|
|
static int print_fident(char* dest, size_t space, FunctionIdentifier id)
|
|
{
|
|
size_t identlen = strlen(tfunc[id].name);
|
|
if ((identlen+1) < space) strcpy(dest, tfunc[id].name);
|
|
else {
|
|
(void)strncpy(dest, tfunc[id].name, space);
|
|
dest[space-1] = '\0';
|
|
}
|
|
return identlen;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* print_string -- print a string value to a dest and return the number of
|
|
characters written
|
|
*/ /*{{{*/
|
|
static int print_string(char* dest, size_t space,
|
|
const char *str, StringFormat sf)
|
|
{
|
|
size_t cur = 0;
|
|
if (sf == QUOTE_STRING && cur < space) dest[cur++] = '"';
|
|
for ( ; cur < space && *str != '\0'; ++str)
|
|
{
|
|
if (sf == QUOTE_STRING && (*str == '"' || *str=='\\')) dest[cur++] = '\\';
|
|
if (cur < space) dest[cur++] = *str;
|
|
}
|
|
if (sf == QUOTE_STRING && cur < space) dest[cur++] = '"';
|
|
return cur;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* printtok -- print a single token, passed by address, although not changed */ /*{{{*/
|
|
size_t ptokatprec(char* dest, size_t size, size_t field_width, StringFormat sf,
|
|
FloatFormat ff, int digits, ErrorFormat ef, const Token *tok,
|
|
FunctionPrecedence atprec)
|
|
{
|
|
if (debug_level > 2) {
|
|
printf("..Entering printtok; bufsize %d, field_width %d, qs %d, ff %s,"
|
|
"prec %d, verr %d\n",
|
|
size, field_width, sf, FloatFormat_Name[ff], digits, ef);
|
|
}
|
|
if (size > 0 ) *dest = '\0';
|
|
if (tok == NULLTOKEN || tok->type == EMPTY) return 0;
|
|
size_t cur = 0;
|
|
switch (tok->type)
|
|
{
|
|
/* STRING */
|
|
case STRING: cur += print_string(dest, size-cur, tok->u.string, sf); break;
|
|
/* INT */ /*{{{*/
|
|
case INT:
|
|
{
|
|
char buf[64];
|
|
size_t buflen;
|
|
|
|
buflen = sprintf(buf, INT_T_FMT, tok->u.integer);
|
|
assert(buflen < sizeof(buf));
|
|
(void)strncpy(dest+cur,buf,size-cur-1);
|
|
cur+=buflen;
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* FLOAT */ /*{{{*/
|
|
case FLOAT:
|
|
{
|
|
/* variables */ /*{{{*/
|
|
char buf[1100], buf2[1100], *use, *p;
|
|
size_t len, len2;
|
|
/*}}}*/
|
|
|
|
if (digits <= 0) digits += FLT_T_DIG;
|
|
if (digits > sizeof(buf) - 20) digits = sizeof(buf) - 20;
|
|
switch (ff) {
|
|
case FLT_DECIMAL:
|
|
len = sprintf(buf, FLT_T_STD_FMT, digits, tok->u.flt);
|
|
use = buf;
|
|
break;
|
|
case FLT_SCIENTIFIC:
|
|
len = sprintf(buf, FLT_T_SCI_FMT, digits, tok->u.flt);
|
|
use = buf;
|
|
break;
|
|
case FLT_COMPACT:
|
|
len = sprintf(buf, FLT_T_CPT_FMT, digits, tok->u.flt);
|
|
/* Unfortunately, %g is so clever that sometimes it omits the
|
|
decimal; it also has a penchant for not switching to scientific
|
|
notation until the magnitude gets very large. So we try to
|
|
counteract these bad tendencies here. If there is no decimal,
|
|
we take the shorter of %.1f and %e with the same number of digits.
|
|
*/
|
|
use = buf;
|
|
if (strchr(buf, '.') == NULL) {
|
|
len = sprintf(buf, FLT_T_STD_FMT, 1, tok->u.flt);
|
|
len2 = sprintf(buf2, FLT_T_SCI_FMT, digits, tok->u.flt);
|
|
/* remove trailing 0s from sci format to be fair */
|
|
size_t epos = strchr(buf2, 'e') - buf2;
|
|
size_t tpos = epos;
|
|
while (tpos > 1 && buf2[tpos-1] == '0' && buf2[tpos-2] != '.') --tpos;
|
|
if (tpos < epos) {
|
|
memmove(buf2+tpos, buf2+epos, len2-epos+1);
|
|
len2 -= epos - tpos;
|
|
}
|
|
if (len2 < len) { use = buf2; len = len2; }
|
|
}
|
|
break;
|
|
case FLT_HEXACT:
|
|
len = sprintf(buf, FLT_T_HEX_FMT, tok->u.flt);
|
|
use = buf;
|
|
break;
|
|
}
|
|
|
|
assert(len < sizeof(buf));
|
|
p = use + len;
|
|
while (*--p == ' ') { *p = '\0'; --len; }
|
|
(void)strncpy(dest+cur, use, size-cur-1);
|
|
cur += len;
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* OPERATOR */ /*{{{*/
|
|
case OPERATOR:
|
|
{
|
|
static const char *ops[]={ "+", "-", "*", "/", "(", ")", ",", "<", "<=", ">=", ">", "==", "~=", "!=", "^", "%" };
|
|
|
|
if ((size-cur)>1)
|
|
{
|
|
dest[cur++]=*ops[tok->u.op];
|
|
if (*(ops[tok->u.op]+1) && size>cur) dest[cur++]=*(ops[tok->u.op]+1);
|
|
}
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* LIDENT */ /*{{{*/
|
|
case LIDENT:
|
|
{
|
|
size_t identlen;
|
|
|
|
identlen=strlen(tok->u.lident);
|
|
if ((cur+identlen+1)<=size) strcpy(dest+cur,tok->u.lident);
|
|
else (void)strncpy(dest+cur,tok->u.lident,size-cur-1);
|
|
cur+=identlen;
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* FIDENT */ /*{{{*/
|
|
case FIDENT:
|
|
if (debug_level > 2) {
|
|
printf("...Found function [%s].\n", tfunc[tok->u.fident].name);
|
|
}
|
|
cur += print_fident(dest+cur, size-cur-1, tok->u.fident);
|
|
break;
|
|
/*}}}*/
|
|
/* LOCATION */ /*{{{*/
|
|
case LOCATION:
|
|
{
|
|
char buf[60];
|
|
|
|
sprintf(buf,"&(%d,%d,%d)",tok->u.location[0],tok->u.location[1],tok->u.location[2]);
|
|
(void)strncpy(dest+cur,buf,size-cur-1);
|
|
cur+=strlen(buf);
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* FUNCALL */ /*{{{*/
|
|
case FUNCALL:
|
|
{
|
|
FunctionIdentifier fid = tok->u.funcall.fident;
|
|
if (tok->u.funcall.argc <= 0)
|
|
{
|
|
cur += print_fident(dest+cur, size-cur-1, fid);
|
|
if (tok->u.funcall.argc == 0)
|
|
{
|
|
if (cur < size - 2) { dest[cur++] = '('; dest[cur++] = ')'; }
|
|
else cur += 2;
|
|
}
|
|
break;
|
|
}
|
|
FunctionPrecedence fp = tfunc[fid].precedence;
|
|
switch (fp)
|
|
{
|
|
case PREFIX_FUNC: {
|
|
cur += print_fident(dest+cur, size-cur-1, fid);
|
|
if (cur < size) dest[cur++] = '(';
|
|
for (size_t ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ai)
|
|
{
|
|
if (ai > 0 && cur < size) dest[cur++] = ',';
|
|
/* The commas eliminate the need for precedence worries in arguments*/
|
|
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
|
|
digits, ef, tok->u.funcall.argv + ai,
|
|
NO_PRECEDENCE);
|
|
}
|
|
if (cur < size) dest[cur++] = ')';
|
|
break;
|
|
}
|
|
case PREFIX_NEG: {
|
|
assert(tok->u.funcall.argc == 1);
|
|
assert(tfunc[fid].display_symbol != NULL);
|
|
if (fp < atprec && cur < size) dest[cur++] = '(';
|
|
if (cur < size) {
|
|
strncpy(dest+cur, tfunc[fid].display_symbol, size-cur-1);
|
|
cur += strlen(tfunc[fid].display_symbol);
|
|
}
|
|
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
|
|
digits, ef, tok->u.funcall.argv, fp);
|
|
if (fp < atprec && cur < size) dest[cur++] = ')';
|
|
break;
|
|
}
|
|
default: /* infix argument */
|
|
assert(tok->u.funcall.argc > 1);
|
|
if (fp < atprec && cur < size) dest[cur++] = '(';
|
|
/* Check for the special relation sequence notation */
|
|
bool specialrel = true;
|
|
if (fid != FUNC_AND || tok->u.funcall.argc < 2) specialrel = false;
|
|
else {
|
|
for (int ai = 0; ai < tok->u.funcall.argc; ++ai) {
|
|
if (tok->u.funcall.argv[ai].type != FUNCALL
|
|
|| tok->u.funcall.argv[ai].u.funcall.argc != 2
|
|
|| !IS_RELATION_FUNC(tok->u.funcall.argv[ai].u.funcall.fident))
|
|
{
|
|
specialrel = false;
|
|
break;
|
|
}
|
|
if (ai > 0
|
|
&& !tok_matches(tok->u.funcall.argv[ai].u.funcall.argv,
|
|
tok->u.funcall.argv[ai-1].u.funcall.argv+1))
|
|
{
|
|
specialrel = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (specialrel) {
|
|
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
|
|
digits, ef, tok->u.funcall.argv, INFIX_BOOL);
|
|
for (int ai = 1; ai < tok->u.funcall.argc && cur < size-1; ++ ai) {
|
|
dest[cur++] = ' ';
|
|
const char *use =
|
|
tfunc[tok->u.funcall.argv[ai].u.funcall.fident].name;
|
|
strncpy(dest+cur, use, size-cur-1);
|
|
cur += strlen(use);
|
|
if (cur < size) dest[cur++] = ' ';
|
|
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
|
|
digits, ef,
|
|
tok->u.funcall.argv[ai].u.funcall.argv + 1,
|
|
INFIX_REL);
|
|
}
|
|
} else {
|
|
for (int ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ai)
|
|
{
|
|
bool parenarg = false;
|
|
if (ai > 0) {
|
|
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
|
|
const char *use = tfunc[fid].display_symbol;
|
|
if (use == NULL) use = tfunc[fid].name;
|
|
strncpy(dest+cur, use, size-cur-1);
|
|
cur += strlen(use);
|
|
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
|
|
} else if (fp > PREFIX_NEG) {
|
|
char powbuf[4096];
|
|
size_t arglen = ptokatprec(powbuf, sizeof(powbuf), 0, sf, ff,
|
|
digits, ef, tok->u.funcall.argv, fp);
|
|
assert(arglen < sizeof(powbuf)-1);
|
|
if (powbuf[0] == '-') {
|
|
parenarg = true;
|
|
if (cur < size) dest[cur++] = '(';
|
|
}
|
|
}
|
|
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
|
|
digits, ef, tok->u.funcall.argv + ai, fp);
|
|
if (parenarg && cur < size) dest[cur++] = ')';
|
|
}
|
|
}
|
|
if (fp < atprec && cur < size) dest[cur++] = ')';
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
/* BOOL */ /*{{{*/
|
|
case BOOL:
|
|
{
|
|
const char *rep = "false";
|
|
if (tok->u.bl) rep = "true";
|
|
strncpy(dest+cur, rep, size-cur-1);
|
|
cur += strlen(rep);
|
|
break;
|
|
}
|
|
/* EEK */ /*{{{*/
|
|
case EEK:
|
|
{
|
|
if (ef == RESTORE_ERROR) {
|
|
strncpy(dest + cur, "error(", size-cur-1);
|
|
cur += 6;
|
|
if (cur < size - 1)
|
|
cur += print_string(dest+cur, size-cur-1, tok->u.err, QUOTE_STRING);
|
|
if (cur < size - 1)
|
|
dest[cur++] = ')';
|
|
break;
|
|
}
|
|
(void)strncpy(dest+cur, _("ERROR"), size-cur-1);
|
|
cur += strlen(_("ERROR"));
|
|
if (ef == VERBOSE_ERROR)
|
|
{
|
|
(void)strncpy(dest+cur, ": ", size-cur-1);
|
|
cur += 2;
|
|
size_t errlen = strlen(tok->u.err);
|
|
if ((cur+errlen+1) < size) strcpy(dest+cur, tok->u.err);
|
|
else (void)strncpy(dest+cur, tok->u.err, size-cur-1);
|
|
cur += errlen;
|
|
}
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* default */ /*{{{*/
|
|
default: assert(0);
|
|
/*}}}*/
|
|
}
|
|
if (cur<size) dest[cur] = 0;
|
|
else
|
|
{
|
|
dest[size-1] = 0;
|
|
cur = size;
|
|
}
|
|
if (field_width && mbslen(dest) > field_width) {
|
|
for (cur = 0; cur < field_width; ++cur) dest[cur] = '#';
|
|
dest[cur] = 0;
|
|
}
|
|
return cur;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* printtok -- print a single token, passed by address, although not changed */ /*{{{*/
|
|
size_t printtok(char* dest, size_t size, size_t field_width,
|
|
StringFormat sf, FloatFormat ff,
|
|
int digits, ErrorFormat ef, const Token *tok)
|
|
{
|
|
return ptokatprec(dest, size, field_width, sf, ff, digits, ef, tok,
|
|
NO_PRECEDENCE);
|
|
}
|
|
/*}}}*/
|
|
|
|
static char dbgpbuf[4096];
|
|
|
|
/* dbgprint -- simple on the fly print for in the debugger */
|
|
const char *dbgprint(const Token* tok) {
|
|
size_t used = printtok(dbgpbuf, sizeof(dbgpbuf), 0, QUOTE_STRING, FLT_COMPACT,
|
|
0, VERBOSE_ERROR, tok);
|
|
if (used > sizeof(dbgpbuf) - 2) {
|
|
printf(_("Warning: buffer overflow in dbgprint"));
|
|
}
|
|
return dbgpbuf;
|
|
}
|
|
|
|
/* print -- print token sequence */ /*{{{*/
|
|
void print(char *s, size_t size, size_t chars, StringFormat sf, FloatFormat ff,
|
|
int digits, Token **n)
|
|
{
|
|
size_t cur;
|
|
|
|
cur=0;
|
|
if (n != EMPTY_TVEC)
|
|
for (; cur<size-1 && (*n) != NULLTOKEN; ++n)
|
|
cur += printtok(s+cur, size-cur, 0, sf, ff, digits, TRUNCATED_ERROR, *n);
|
|
if (cur<size) s[cur] = 0;
|
|
else s[size-1] = 0;
|
|
if (chars && mbslen(s) > chars) {
|
|
for (cur=0; cur < chars; ++cur) s[cur] = '#';
|
|
s[cur] = 0;
|
|
}
|
|
}
|
|
/*}}}*/
|