Streamline internals of teapot

The primary change is to add a “funcall” token, so that an entire expression
  can be encapsulated as a single token. This change is used to allow a cell to
  include simply a selection of appropriate semantic tokens. I.e., the content
  and iterative content are now each a single token like the value and the
  result value. Token vectors are used only as intermediate results in scanning
  and parsing.

  Not this means the cells are now in effect storing parse trees, so
  computation should be slightly faster and future extensions (like #56) should
  be facilitated.

  This commit also takes the opportunity while internals are being altered to
  add another token to a cell for future use for computed attributes, cf #22,
  and to change the internal numerical values from double and ints to long
  doubles and long longs. However, the change attempts to encapsulate that
  choice so it would be easy to change back or change to another representation.

  Note that these changes break savexdr(), as the internal binary format of
  a cell is now different. Rather than reimplement it, it is deprecated as
  the world does not need another binary spreadsheet format. Hence, the
  ascii format for teapot spreadsheets becomes the primary file format.
  Loading of old xdr files is still supported for backward compatibility.

  Closes #59.

  Also along the way, various other slight fixes and enhancements crept in,
  a partial but probably not exhaustive list of which follows:

  Fixes #31.

  Further revisions and improvements to documentation.

  Make the approximate comparison of floating point values scale more
  accurately with the size of the doubles being compared.

  Further extensions of absolute and relative cell addressing.

  Addition of (circle constant) tau function/constant.

  Modified string conversion to simply use internal printing routines, and
  to take "scientific" and "decimal" keywords.

  Allowed n() function to take a list of values, or just a single location
  defaulting to the current location.

  Added floor, ceil, trunc, and round functions, and allowed them to be
  keywords controlling the int() integer conversion function as well.

  Allowed substr() to drop its last argument to go to the end of the string.

  Provided an enum of built-in functions to preserve legacy function
  identifiers, allowing the large table inside func.c to be reorganized
  in a clearer fashion.

  Added additional annotation of properties of the built-in functions,
  including precedence.

  All operators are now also accessible as built-in functions.

  Made precedence of unary - lower than ^ to match python.

  Avoided inadvertently using FLTK @symbol abbreviations for formulas with
  "@" in them.
This commit is contained in:
Glen Whitney 2019-08-23 12:12:06 -07:00
parent a56efa0c91
commit 364ef2c0ea
22 changed files with 2611 additions and 1651 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,39 +7,52 @@
#include "eval.h"
#include "main.h"
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)
{
(void)memset(fresh->contents, 0, sizeof(fresh->contents));
fresh->label=(char*)0;
fresh->adjust=AUTOADJUST;
fresh->precision=-1;
fresh->shadowed=0;
fresh->bold=0;
fresh->underline=0;
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
fresh->tok[tv].type = EMPTY;
fresh->label = NULL;
fresh->precision = -1;
fresh->adjust = AUTOADJUST;
for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a)
fresh->aspect[a] = DefaultCN[a];
fresh->scientific=DEF_SCIENTIFIC;
fresh->value.type=EMPTY;
fresh->resvalue.type=EMPTY;
fresh->locked=0;
fresh->ignored=0;
fresh->clock_t0=0;
fresh->clock_t1=0;
fresh->clock_t2=0;
fresh->updated = 0;
fresh->shadowed = 0;
fresh->scientific = DEF_SCIENTIFIC;
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 **getcont(const Cell *cell, ContentVariety v)
Token gettok(const Cell *cell, TokVariety v)
{
if (cell == NULLCELL) return EMPTY_TVEC;
Token emp;
emp.type = EMPTY;
if (cell == NULLCELL) return emp;
if (v == CONTINGENT)
v = (cell->clock_t0 && cell->contents[ITERATIVE]) ? ITERATIVE : BASE;
return cell->contents[v];
v = (cell->clock_t0 && cell->tok[ITER_CONT].type != EMPTY)
? ITER_CONT : BASE_CONT;
return cell->tok[v];
}
/* getadjust -- get cell adjustment */
@ -47,7 +60,8 @@ Adjust getadjust(const Cell* cell)
{
if (cell == NULLCELL) return LEFT;
else if (cell->adjust == AUTOADJUST)
return (cell->value.type == INT || cell->value.type == FLOAT ? RIGHT : LEFT);
return (cell->tok[CURR_VAL].type == INT
|| cell->tok[CURR_VAL].type == FLOAT) ? RIGHT : LEFT;
else return cell->adjust;
}
@ -148,17 +162,13 @@ static void copytokens(Token*** totoks, Token** fromtoks)
/* freecellcontents -- free the resources of the cell at destruction time */
void freecellcontents(Cell *faded)
{
tvecfree(faded->contents[BASE]);
tvecfree(faded->contents[ITERATIVE]);
tfree(&(faded->value));
tfree(&(faded->resvalue));
if (faded->label != (char*)0) {
free(faded->label);
}
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)
void copycell(Cell *to, const Cell *fromcell, LabelHandling lh)
{
assert(to != NULLCELL);
if (to == fromcell) return;
@ -166,16 +176,15 @@ void copycell(Cell *to, const Cell *fromcell)
freecellcontents(to);
if (fromcell != NULLCELL) {
memcpy(to, fromcell, sizeof(Cell));
copytokens(&(to->contents[BASE]), fromcell->contents[BASE]);
copytokens(&(to->contents[ITERATIVE]), fromcell->contents[ITERATIVE]);
if (fromcell->label != (char*)0) {
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';
}
to->value.type = EMPTY;
to->resvalue.type = EMPTY;
} else {
initcellcontents(to);
}

View File

@ -9,23 +9,26 @@ extern "C" {
typedef enum { LEFT=0, RIGHT=1, CENTER=2, AUTOADJUST=3 } Adjust;
typedef enum { BASE=0, ITERATIVE=1, CONTINGENT=2 } ContentVariety;
typedef enum
{ BASE_CONT=0, ITER_CONT, ATTR_REF, MAX_PERSIST_TV = ATTR_REF,
CURR_VAL, RES_VAL,
CONTINGENT /* Must be last, marks end of many loops */
} TokVariety;
extern const char *TokVariety_Name[];
typedef unsigned char ColorNum;
#define MAX_MAX_COLORS UCHAR_MAX;
typedef enum { FOREGROUND = 0, BACKGROUND, NUM_COLOR_ASPECTS } ColorAspect;
extern const char *ColorAspect_Name[];
extern const ColorNum DefaultCN[];
typedef struct
{
Token **contents[CONTINGENT];
Token tok[CONTINGENT];
char *label;
Token value;
Token resvalue;
Adjust adjust;
int precision;
Adjust adjust;
ColorNum aspect[NUM_COLOR_ASPECTS];
unsigned int updated:1;
unsigned int shadowed:1;
@ -42,7 +45,9 @@ typedef struct
#define NULLCELL ((Cell*)0)
Token **getcont(const Cell *cell, ContentVariety v);
typedef enum {ALTER_LABEL, PRESERVE_LABEL} LabelHandling;
Token gettok(const Cell *cell, TokVariety v);
Adjust getadjust(const Cell *cell);
bool shadowed(const Cell *cell);
bool isbold(const Cell *cell);
@ -56,7 +61,7 @@ int getprecision(const Cell* cell);
const char *getlabel(const Cell *cell);
void initcellcontents(Cell *cell);
void freecellcontents(Cell *cell);
void copycell(Cell *to, const Cell *from);
void copycell(Cell *to, const Cell *from, LabelHandling lh);
#ifdef __cplusplus
}

View File

@ -17,6 +17,7 @@
#endif
#include "csv.h"
#include "scanner.h"
static int semicol=0;
@ -77,15 +78,15 @@ void csv_separator(const char *s, const char **end)
}
/*}}}*/
/* csv_long -- convert string [0[x]]12345 to long */ /*{{{*/
long csv_long(const char *s, const char **end)
IntT csv_long(const char *s, const char **end)
{
long value;
IntT value;
const char *t;
assert(s!=(const char*)0);
assert(end!=(const char**)0);
if (*s=='\t') { *end=s; return 0L; };
value=strtol(s,(char**)end,0);
value = STRTOINT(s, (char**)end, 0);
if (s!=*end)
{
t=*end; csv_separator(t,end);
@ -95,15 +96,15 @@ long csv_long(const char *s, const char **end)
}
/*}}}*/
/* csv_double -- convert string 123.4e5 to double */ /*{{{*/
double csv_double(const char *s, const char **end)
FltT csv_double(const char *s, const char **end)
{
double value;
FltT value;
const char *t;
assert(s!=(const char*)0);
assert(end!=(const char**)0);
if (*s=='\t') { *end=s; return 0.0; };
value=strtod(s,(char**)end);
value = STRTOFLT(s,(char**)end);
if (s!=*end)
{
t=*end; csv_separator(t,end);
@ -112,6 +113,7 @@ double csv_double(const char *s, const char **end)
*end=s; return 0.0;
}
/*}}}*/
/* csv_string -- convert almost any string to string */ /*{{{*/
char *csv_string(const char *s, const char **end)
{

View File

@ -1,6 +1,8 @@
#ifndef CSV_H
#define CSV_H
#include "scanner.h"
struct CSV_Date
{
int day;
@ -11,7 +13,7 @@ struct CSV_Date
void csv_setopt(int sem);
void csv_separator(const char *s, const char **end);
char *csv_string(const char *s, const char **end);
double csv_double(const char *s, const char **end);
long csv_long(const char *s, const char **end);
FltT csv_double(const char *s, const char **end);
IntT csv_long(const char *s, const char **end);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,56 @@
#ifndef FUNC_H
#define FUNC_H
#include "scanner.h"
#define MAX_FUNC_NAME_LENGTH 20
typedef enum
/* The purpose of this enum is to enforce backward compatibility
of numeric function identifiers which may be recorded in old
save files. Hence, do NOT change the order of the items in this
enum, merely add new identifiers at the end, just before
N_FUNCTION_IDENTIFIERS.
*/
{
NOT_A_FUNCTION = -1,
FIRST_FUNCTION = 0,
FUNC_AT_SYMBOL = 0, FUNC_AMPERSAND, FUNC_X, FUNC_Y, FUNC_Z, FUNC_EVAL,
FUNC_ERROR, FUNC_STRING, FUNC_SUM, FUNC_N, FUNC_INT, FUNC_FRAC, FUNC_LEN,
FUNC_MIN, FUNC_MAX, FUNC_ABS, FUNC_DOLLAR_SIGN, FUNC_FLOAT, FUNC_STRFTIME,
FUNC_CLOCK, FUNC_POLY, FUNC_E, FUNC_LOG, FUNC_SIN, FUNC_COS, FUNC_TAN,
FUNC_SINH, FUNC_COSH, FUNC_TANH, FUNC_ASIN, FUNC_ACOS, FUNC_ATAN,
FUNC_ARSINH, FUNC_ARCOSH, FUNC_ARTANH, FUNC_DEG2RAD, FUNC_RAD2DEG,
FUNC_RND, FUNC_SUBSTR, FUNC_STRPTIME, FUNC_TIME, FUNC_BITAND, FUNC_BITOR,
FUNC_R, FUNC_D, FUNC_CAP_X, FUNC_X_AMPERSAND, FUNC_NEGATE, FUNC_PLUS_SYMBOL,
FUNC_MINUS_SYMBOL, FUNC_ASTERISK, FUNC_SLASH,
FUNC_LESS_EQUAL, FUNC_GREATER_EQUAL, FUNC_LESS_THAN, FUNC_GREATER_THAN,
FUNC_EQUAL_EQUAL, FUNC_TILDE_EQUAL, FUNC_BANG_EQUAL, FUNC_CARET,
FUNC_PER_CENT, FUNC_CONCAT, FUNC_TAU, FUNC_SQRT, FUNC_FLOOR, FUNC_CEIL,
FUNC_TRUNC, FUNC_ROUND, FUNC_DECIMAL, FUNC_SCIENTIFIC,
N_FUNCTION_IDS
} FunctionIdentifier;
/* Forward declaration of Token, since this header is used in scanner.h */
typedef struct Token_struc Token;
typedef enum /* In increasing order of precedence */
{
INFIX_CONC, INFIX_REL, INFIX_PLUS, INFIX_MUL, PREFIX_NEG,
INFIX_POW, PREFIX_FUNC
} FunctionPrecedence;
typedef enum { FUNCT, MACRO } EvaluationStrategy;
typedef struct
{
const char name[20];
Token (*func)(int, const Token[]);
const char name[MAX_FUNC_NAME_LENGTH + 1];
Token (*func)(int, const Token*);
FunctionPrecedence precedence;
EvaluationStrategy eval_as;
const char* display_symbol;
} Tfunc;
FunctionIdentifier identcode(const char *s, size_t len);
extern Tfunc tfunc[];
#endif

View File

@ -43,6 +43,7 @@ extern char *strdup(const char* s);
#include "parser.h"
#include "sheet.h"
#include "wk1.h"
#include "xdr.h"
/*}}}*/
/* variables */ /*{{{*/
@ -157,7 +158,7 @@ int doanyway(Sheet *sheet, const char *msg)
/*}}}*/
/* do_edit -- set or modify cell contents */ /*{{{*/
static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv)
static int do_edit(Sheet *cursheet, Key c, const char *expr, TokVariety tv)
{
/* variables */ /*{{{*/
char buf[1024];
@ -165,6 +166,7 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv)
size_t x,offx;
Location scur;
Token **t;
Token newcont;
Cell *cell;
/*}}}*/
@ -172,37 +174,48 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv)
if (locked(cell)) line_msg(_("Edit cell:"),_("Cell is locked"));
else
{
newcont.type = EMPTY;
LOCATION_GETS(scur, cursheet->cur);
if (expr)
{
s=expr;
t=scan(&s);
s = expr;
t = scan(&s);
prompt = _("Cell contents:");
if (cv == ITERATIVE) prompt = _("Clocked cell contents");
if (*s!='\0' && t==(Token**)0) line_msg(prompt, "XXX invalid expression");
if (tv == ITER_CONT) prompt = _("Clocked cell contents");
if (*s != '\0')
if (t == EMPTY_TVEC)
line_msg(prompt, "XXX invalid expression");
else
{
newcont = eval_safe(t, LITERAL);
if (newcont.type = EEK)
line_msg(prompt, "XXX unparseable expression");
}
tvecfree(t);
}
else
{
Token cntt;
offx=0;
if (c==K_NONE)
{
print(buf, sizeof(buf), 0, 1, getscientific(cell), -1,
getcont(cell, cv));
s=buf+strlen(buf);
cntt = gettok(cell, tv);
printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt);
s = buf+strlen(buf);
}
else if (c==K_BACKSPACE)
{
print(buf, sizeof(buf), 0, 1, getscientific(cell), -1,
getcont(cell, cv));
cntt = gettok(cell, tv);
printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt);
if (strlen(buf)) *mbspos(buf+strlen(buf),-1)='\0';
s=buf+strlen(buf);
s = buf+strlen(buf);
}
else if (c==K_DC)
{
print(buf, sizeof(buf), 0, 1, getscientific(cell), -1,
getcont(cell, cv));
cntt = gettok(cell, tv);
printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt);
memmove(buf,mbspos(buf,1),strlen(mbspos(buf,1))+1);
s=buf;
s = buf;
}
else if (isalpha(c))
{
@ -222,22 +235,30 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv)
{
int r;
x=mbslen(buf)-mbslen(s);
tfree(&newcont);
newcont.type = EEK;
newcont.u.err = (char *)0;
x = mbslen(buf)-mbslen(s);
prompt = _("Cell contents:");
if (cv == ITERATIVE) prompt = _("Clocked cell contents:");
if (tv == ITER_CONT) prompt = _("Clocked cell contents:");
if ((r = line_edit(cursheet, buf, sizeof(buf), prompt, &x, &offx)) < 0)
return r;
s = buf;
if (buf[0] == '"' && buf[strlen(buf)-1] != '"' && strlen(buf)+1 < sizeof(buf)) {
buf[strlen(buf)+1] = 0;
buf[strlen(buf)] = '"';
if (buf[0] == '"' && buf[strlen(buf)-1] != '"'
&& strlen(buf)+1 < sizeof(buf))
{
buf[strlen(buf)+1] = 0;
buf[strlen(buf)] = '"';
}
t=scan(&s);
} while (*s!='\0' && t==(Token**)0);
t = scan(&s);
if (t != EMPTY_TVEC) {
newcont = eval_safe(t, LITERAL);
}
tvecfree(t);
} while (*s != '\0' && newcont.type == EEK);
}
if (t!=(Token**)0 && *t==(Token*)0) { free(t); t=(Token**)0; }
movetoloc(cursheet, scur);
putcont(cursheet, cursheet->cur, t, cv);
puttok(cursheet, cursheet->cur, newcont, tv);
forceupdate(cursheet);
}
return 0;
@ -285,9 +306,11 @@ static int do_label(Sheet *sheet)
}
} while (!ok);
setlabel(sheet, sheet->cur, buf,1);
if (buf[0]!='\0')
if (buf[0] != '\0' && oldlabel[0] != '\0')
for (ALL_LOCS_IN_REGION(sheet,w))
relabel(sheet, w, oldlabel, buf);
cachelabels(sheet);
forceupdate(sheet);
}
return -1;
}
@ -1217,7 +1240,8 @@ void fillwith(Sheet *she)
Location dest;
bool scan_labels = (src != NULLCELL && getlabel(src) != (const char*)0);
/* the following is safe since we have handled copying a cell to itself */
for (ALL_LOCS_IN_REGION(she, dest)) copycelltosheet(src, she, dest);
for (ALL_LOCS_IN_REGION(she, dest))
copycelltosheet(src, she, dest, ALTER_LABEL);
if (scan_labels) cachelabels(she);
forceupdate(she);
}
@ -1508,7 +1532,7 @@ static int do_goto(Sheet *sheet, const char *expr)
LOCATION_GETS(upd_l, sheet->cur);
upd_sheet = sheet;
value = eval(t, FULL);
value = eval_safe(t, FULL);
tvecfree(t);
if (value.type==LOCATION && IN_OCTANT(value.u.location))
movetoloc(sheet, value.u.location);
@ -1851,9 +1875,10 @@ int main(int argc, char *argv[])
char *end;
n=strtol(optarg,&end,0);
if (*end || n<0 || n>DBL_DIG)
if (*end || n < 0 || n > LDBL_DIG)
{
fprintf(stderr,_("teapot: precision must be between 0 and %d.\n"),DBL_DIG);
fprintf(stderr,
_("teapot: precision must be between 0 and %d.\n"), LDBL_DIG);
exit(1);
}
def_precision=n;

View File

@ -97,7 +97,7 @@ static void catchfpe(int n)
caughtfpe=1;
}
const char *dblfinite(double x)
const char *dblfinite(FltT x)
{
/*struct sigaction act;
@ -118,18 +118,19 @@ const char *dblfinite(double x)
/* If one comparison was allowed, more won't hurt either. */
if (x<0.0)
{
if (x<-DBL_MAX) return _("Not a (finite) floating point number"); /* -infinite */
if (x < -FLTMX) return _("Not a (finite) floating point number"); /* -infinite */
else return (const char*)0;
}
else if (x>0.0)
{
if (x>DBL_MAX) return _("Not a (finite) floating point number"); /* +infinite */
if (x > FLTMX) return _("Not a (finite) floating point number"); /* +infinite */
else return (const char*)0;
}
else return _("Not a (finite) floating point number"); /* NaN */
}
}
/*}}}*/
/* fputc_close -- error checking fputc which closes stream on error */ /*{{{*/
int fputc_close(char c, FILE *fp)
{

View File

@ -11,7 +11,7 @@ extern "C" {
void posorder(int *x, int *y);
long int posnumber(const char *s, const char **endptr);
const char *dblfinite(double x);
const char *dblfinite(FltT x);
int fputc_close(char c, FILE *fp);
int fputs_close(const char *s, FILE *fp);
void adjust(Adjust a, char *s, size_t n);

View File

@ -20,6 +20,7 @@ extern char *strdup(const char* s);
#include "eval.h"
#include "func.h"
#include "main.h"
#include "misc.h"
#include "parser.h"
@ -34,6 +35,27 @@ extern char *strdup(const char* s);
static Token term(Token *n[], int *i, EvalMethod meth);
/*}}}*/
/* full_eval_funcall -- evaluate the args of a funcall token and then
call the function on them
*/
static Token full_eval_funcall(Token *t)
{
assert(t->type == FUNCALL);
if (t->u.funcall.argc < 1)
return tfuncall(t->u.funcall.fident, t->u.funcall.argc, 0);
Token *eval_argv = malloc(t->u.funcall.argc*sizeof(Token));
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) {
eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL);
}
Token result = tfuncall(t->u.funcall.fident, t->u.funcall.argc, eval_argv);
/* To allow a function to return one of its arguments, we need
to be sure not to free that argument: */
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai)
tfree_protected(&eval_argv[ai], result);
free(eval_argv);
return result;
}
/* primary -- parse and evaluate a primary term */ /*{{{*/
static Token primary(Token *n[], int *i, EvalMethod meth)
{
@ -87,19 +109,7 @@ static Token primary(Token *n[], int *i, EvalMethod meth)
duperror(&result, _(") expected"));
return result;
/*}}}*/
case MINUS: /* return negated term */ /*{{{*/
{
++(*i);
if (meth == FULL) return tneg(primary(n, i, meth));
Token arg = primary(n, i, meth);
if (arg.type == EEK) return arg;
result.type = FUNCALL;
result.u.funcall.fident = identcode("negate", 6);
result.u.funcall.argc = 1;
result.u.funcall.argv = malloc(sizeof(Token));
result.u.funcall.argv[0] = arg;
return result;
}
/* Unary minus will be handled in powterm */
case CP:
duperror(&result, _("Extra umatched ')'"));
return result;
@ -200,18 +210,16 @@ static Token primary(Token *n[], int *i, EvalMethod meth)
if (argc > 0)
{
result.u.funcall.argv = malloc(argc*sizeof(Token));
for (size_t ai; ai < argc; ++ai)
for (size_t ai = 0; ai < argc; ++ai)
result.u.funcall.argv[ai] = argv[ai];
}
} else result.u.funcall.argv = NULLTOKEN;
return result;
}
/*}}}*/
/* FUNCALL */ /*{{{*/
case FUNCALL:
if (meth == FULL)
result = tfuncall(n[*i]->u.funcall.fident, n[*i]->u.funcall.argc,
n[*i]->u.funcall.argv);
if (meth == FULL) result = full_eval_funcall(n[*i]);
else result = tcopy(*n[*i]);
++(*i);
return result;
@ -230,9 +238,45 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
Token l;
size_t npows = 0;
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == MINUS)
{
/* A - symbol here is a pain. If it is being used as a function symbol, it
is higher precedence than exponentiation. If it is unary negation,
then it's lower precedence and we have to grab a powterm to the right,
and negate it. As far as I can tell the only way to tell is to
look ahead a term to see if there's a comma...
*/
bool unaryneg = true;
int j = *i + 1;
if (n[j] == NULLTOKEN)
return duperror(&l, _("A bare - is not a valid expression"));
if (n[j]->type == OPERATOR && n[j]->u.op == OP)
{
++j;
Token dummy = term(n, &j, meth);
if (n[j] != NULLTOKEN && n[j]->type == OPERATOR && n[j]->u.op == COMMA)
unaryneg = false;
tfree(&dummy);
}
if (unaryneg)
{
++(*i);
l = powterm(n, i, meth);
if (meth == FULL) return tneg(l);
if (l.type == EEK) return l;
if (TOKISNUM(l)) return tneg(l);
Token result;
result.type = FUNCALL;
result.u.funcall.fident = FUNC_NEGATE;
result.u.funcall.argc = 1;
result.u.funcall.argv = malloc(sizeof(Token));
result.u.funcall.argv[0] = l;
return result;
}
}
l = primary(n, i, meth);
if (l.type == EEK) return l;
while (n[*i] != (Token*)0 && n[*i]->type == OPERATOR && n[*i]->u.op == POW)
while (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == POW)
{
Token r;
@ -255,7 +299,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
{
Token tmp = l;
l.type = FUNCALL;
l.u.funcall.fident = identcode("^", 1);
l.u.funcall.fident = FUNC_CARET;
l.u.funcall.argc = 1;
l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
l.u.funcall.argv[0] = tmp;
@ -278,7 +322,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
/* piterm -- parse and evaluate a product/division/modulo term */ /*{{{*/
static Token piterm(Token *n[], int *i, EvalMethod meth)
{
int mulident = identcode("*", 1);
FunctionIdentifier mulident = FUNC_ASTERISK;
Token l;
Operator op = CP;
bool first_funcall = true;
@ -317,6 +361,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
{
first_funcall = false;
Token tmp = l;
l.type = FUNCALL;
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
l.u.funcall.argc = 2;
if (op == MUL) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
@ -344,7 +389,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
/* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/
static Token factor(Token *n[], int *i, EvalMethod meth)
{
int plusident = identcode("+", 1);
FunctionIdentifier plusident = FUNC_PLUS_SYMBOL;
Token l;
Operator op = CP;
bool first_funcall = true;
@ -376,6 +421,7 @@ static Token factor(Token *n[], int *i, EvalMethod meth)
{
first_funcall = false;
Token tmp = l;
l.type = FUNCALL;
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
l.u.funcall.argc = 2;
if (op == PLUS) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
@ -453,14 +499,15 @@ static Token term(Token *n[], int *i, EvalMethod meth)
}
/*}}}*/
/* eval -- parse and evaluate token sequence */ /*{{{*/
/* eval -- parse and evaluate nonempty token sequence
if the sequence might be empty, use eval_safe. */ /*{{{*/
Token eval(Token **n, EvalMethod meth)
{
Token l;
int i = 0;
bool first_funcall = true;
assert(upd_sheet != (Sheet*)0);
assert(meth == LITERAL || upd_sheet != (Sheet*)0);
l = term(n, &i, meth);
if (l.type == EEK) return l;
@ -485,10 +532,11 @@ Token eval(Token **n, EvalMethod meth)
{
first_funcall = false;
Token tmp = l;
l.u.funcall.fident = identcode("concat", 6);
l.type = FUNCALL;
l.u.funcall.fident = FUNC_CONCAT;
l.u.funcall.argc = 1;
l.u.funcall.argv = malloc(MAXARGC*sizeof(Token));
l.u.funcall.argv[0] = l;
l.u.funcall.argv[0] = tmp;
}
if (l.u.funcall.argc >= MAXARGC)
{
@ -508,10 +556,49 @@ Token eval(Token **n, EvalMethod meth)
Token eval_safe(Token **n, EvalMethod meth)
{
Token result;
if (n == EMPTY_TVEC)
if (n == EMPTY_TVEC || *n == NULLTOKEN)
{
result.type = EMPTY;
return result;
}
return eval(n, meth);
}
/* evaltoken -- like eval, but evaluate a single token
NOTE: caller owns the result and must arrrange that it be
eventually tfree()ed */ /*{{{*/
Token evaltoken(Token n, EvalMethod meth)
{
if (meth == LITERAL) return tcopy(n);
switch (n.type) {
case EMPTY: return n;
case STRING: return tcopy(n);
case FLOAT:
case INT:
return n;
case OPERATOR: {
Token err;
err.type = EEK;
const char *templ = _("Attempt to eval bare operator token: ");
err.u.err = malloc(strlen(templ) + MAX_OP_NAME_LENGTH + 2);
strcpy(err.u.err, templ);
strcat(err.u.err, Op_Name[n.u.op]);
return err;
}
case LIDENT: return findlabel(upd_sheet, n.u.lident);
case FIDENT: {
Token err;
err.type = EEK;
const char *templ = _("Attempt to eval bare function identifier token: ");
err.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 2);
strcpy(err.u.err, templ);
strcat(err.u.err, tfunc[n.u.fident].name);
return err;
}
case LOCATION: return n;
case FUNCALL: return full_eval_funcall(&n);
case EEK: return tcopy(n);
default: assert(0);
}
return n;
}

View File

@ -17,8 +17,8 @@
#include <stdlib.h>
#include <string.h>
#include "eval.h"
#include "parser.h"
#include "main.h"
#include "sheet.h"
#include "sc.h"
@ -224,7 +224,7 @@ const char *loadsc(Sheet *sheet, const char *name)
width=strlen(buf);
if (width>0 && buf[width-1]=='\n') buf[width-1]='\0';
/*}}}*/
if (buf[0] && buf[0]!='#')
if (buf[0] && buf[0] != '#')
{
if (strncmp(buf,"format ",7)==0) /* format col width precision whoknows */ /*{{{*/
{
@ -241,28 +241,40 @@ const char *loadsc(Sheet *sheet, const char *name)
setwidth(sheet, col, 0, colwidth);
}
/*}}}*/
else if (strncmp(buf,"leftstring ",11)==0 || strncmp(buf,"rightstring ",12)==0) /* rightstring/leftstring cell = "string" */ /*{{{*/
else if (strncmp(buf, "leftstring ", 11) == 0
|| strncmp(buf, "rightstring ", 12) == 0) /* rightstring/leftstring cell = "string" */ /*{{{*/
{
int x,y;
const char *s;
Token **contents;
if (strncmp(buf,"leftstring ",11)==0) s=buf+11; else s=buf+12;
x=*s++-'A'; if (*s>='A' && *s<='Z') x=x*26+(*s++-'A');
y=*s++-'0'; while (*s>='0' && *s<='9') y=10*y+(*s++-'0');
if (strncmp(buf, "leftstring ", 11) == 0) s = buf+11; else s = buf+12;
x = *s++ - 'A';
if (*s >= 'A' && *s <= 'Z' ) x = x*26 + (*s++ - 'A');
y = *s++ - '0';
while (*s >= '0' && *s <= '9') y = 10*y + (*s++-'0');
s+=3;
contents=scan(&s);
if (contents==(Token**)0)
contents = scan(&s);
if (contents == EMPTY_TVEC)
{
tvecfree(contents);
sprintf(errbuf,_("Expression syntax error in line %d"),line);
err=errbuf;
sprintf(errbuf, _("Expression syntax error in line %d"), line);
err = errbuf;
goto eek;
}
tmp[X] = x; tmp[Y] = y; tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
cell->adjust = strncmp(buf,"leftstring ",11) ? RIGHT : LEFT;
cell->contents[BASE] = contents;
cell->adjust = strncmp(buf, "leftstring ", 11) ? RIGHT : LEFT;
cell->tok[BASE_CONT] = eval_safe(contents, LITERAL);
tvecfree(contents);
if (cell->tok[BASE_CONT].type == EEK)
{
sprintf(errbuf, _("Parse error in line %d: %s"),
line, cell->tok[BASE_CONT].u.err);
tfree(&(cell->tok[BASE_CONT]));
err = errbuf;
goto eek;
}
}
/*}}}*/
else if (strncmp(buf,"let ",4)==0) /* let cell = expression */ /*{{{*/
@ -277,22 +289,22 @@ const char *loadsc(Sheet *sheet, const char *name)
x=*s++-'A'; if (*s>='A' && *s<='Z') x=x*26+(*s++-'A');
y=*s++-'0'; while (*s>='0' && *s<='9') y=10*y+(*s++-'0');
tmp[X] = x; tmp[Y] = y; tmp[Z] = 0;
if (getcont(CELL_ATC(sheet,x,y,0),BASE)==(Token**)0)
if (gettok(CELL_ATC(sheet,x,y,0), BASE_CONT).type == EMPTY)
{
s+=3;
s2t_s=s; s2t_t=newbuf;
if (s2t_term(sheet)==0)
s += 3;
s2t_s = s; s2t_t = newbuf;
if (s2t_term(sheet) == 0)
{
*s2t_t='\0';
if (err==(const char*)0)
*s2t_t = '\0';
if (err == (const char*)0)
{
sprintf(errbuf,_("Unimplemented SC feature in line %d"),line);
err=errbuf;
sprintf(errbuf, _("Unimplemented SC feature in line %d"),line);
err = errbuf;
}
}
*s2t_t='\0';
s=newbuf;
contents=scan(&s);
*s2t_t = '\0';
s = newbuf;
contents = scan(&s);
if (contents==(Token**)0)
{
tvecfree(contents);
@ -303,7 +315,16 @@ const char *loadsc(Sheet *sheet, const char *name)
tmp[X] = x; tmp[Y] = y; tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
cell->adjust = RIGHT;
cell->contents[BASE] = contents;
cell->tok[BASE_CONT] = eval_safe(contents, LITERAL);
tvecfree(contents);
if (cell->tok[BASE_CONT].type == EEK)
{
sprintf(errbuf, _("Parse error in line %d: %s"),
line, cell->tok[BASE_CONT].u.err);
tfree(&(cell->tok[BASE_CONT]));
err = errbuf;
goto eek;
}
}
}
/*}}}*/

View File

@ -18,6 +18,7 @@
#include <stdlib.h>
extern char *strdup(const char* s);
extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */
extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */
#include <string.h>
@ -43,18 +44,6 @@ const char *Op_Name[] =
[POW] = "^", [MOD] = "%"
};
/* identcode -- return number of identifier */ /*{{{*/
int identcode(const char *s, size_t len)
{
Tfunc *p;
int fident;
for (p=tfunc,fident=0; p->name[0]!='\0' && (len!=strlen(p->name) || strncmp(s,p->name,len)); ++p,++fident);
if (p->name[0]=='\0') return -1;
else return fident;
}
/*}}}*/
/* loc_in_box -- returns true if test is in the box determined by b and c */
bool loc_in_box(const Location test,
const Location b, const Location c)
@ -67,10 +56,21 @@ bool loc_in_box(const Location test,
return true;
}
/* cleartoken - Initialize all of the memory of a token */ /*{{{*/
void cleartoken(Token* tok)
{
tok->type = EMPTY;
tok->u.location[0] = 0;
tok->u.location[1] = 0;
tok->u.location[2] = 0;
}
/* duperror - Sets tok to an error and strdups the message into place */
void duperror(Token* tok, const char* erro) {
Token duperror(Token* tok, const char* erro)
{
tok->type = EEK;
tok->u.err = strdup(erro);
return *tok;
}
/* charstring -- match quoted string and return token */ /*{{{*/
@ -103,6 +103,7 @@ static Token *charstring(const char **s)
else return (Token*)0;
}
/*}}}*/
/* integer -- match an unsigned integer and return token */ /*{{{*/
static Token *integer(const char **s)
{
@ -123,6 +124,7 @@ static Token *integer(const char **s)
else { *s=r; return (Token*)0; }
}
/*}}}*/
/* flt -- match a floating point number */ /*{{{*/
static Token *flt(const char **s)
{
@ -130,11 +132,11 @@ static Token *flt(const char **s)
const char *t;
char *end;
Token *n;
double x;
FltT x;
/*}}}*/
t=*s;
x=strtod(t,&end);
x = STRTOFLT(t,&end);
*s = end;
if (t!=*s && dblfinite(x)==(const char*)0)
{
@ -150,6 +152,7 @@ static Token *flt(const char **s)
}
}
/*}}}*/
/* op -- match an op and return token */ /*{{{*/
static Token *op(const char **s)
{
@ -194,7 +197,7 @@ static Token *ident(const char **s)
begin=*s; ++(*s);
while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s);
result=malloc(sizeof(Token));
if ((fident=identcode(begin,(size_t)(*s-begin)))==-1)
if ((fident = identcode(begin,(size_t)(*s-begin))) == NOT_A_FUNCTION)
{
result->type=LIDENT;
result->u.lident=malloc((size_t)(*s-begin+1));
@ -218,43 +221,43 @@ Token **scan(const char **s)
/* variables */ /*{{{*/
Token **na,*n;
const char *r;
int i,j;
/*}}}*/
/* compute number of tokens */ /*{{{*/
r=*s;
while (*r==' ') ++r;
for (i=0; *r!='\0'; ++i)
r = *s;
while (*r == ' ') ++r;
int i = 0;
for (; *r != '\0'; ++i)
{
const char *or;
or=r;
while (*r==' ') ++r;
n=charstring(&r);
if (n==(Token*)0) n=op(&r);
if (n==(Token*)0) n=integer(&r);
if (n==(Token*)0) n=flt(&r);
if (n==(Token*)0) n=ident(&r);
if (or==r) { *s=r; return (Token**)0; }
or = r;
while (*r == ' ') ++r;
n = charstring(&r);
if (n == NULLTOKEN) n = op(&r);
if (n == NULLTOKEN) n = integer(&r);
if (n == NULLTOKEN) n = flt(&r);
if (n == NULLTOKEN) n = ident(&r);
if (or == r) { *s = r; return EMPTY_TVEC; }
}
/*}}}*/
/* allocate token space */ /*{{{*/
na=malloc(sizeof(Token*)*(i+1));
na = malloc(sizeof(Token*)*(i+1));
/*}}}*/
/* store tokens */ /*{{{*/
r=*s;
r = *s;
while (*r==' ') ++r;
for (j=0; j<i; ++j)
for (int j = 0; j < i; ++j)
{
while (*r==' ') ++r;
n=charstring(&r);
if (n==(Token*)0) n=op(&r);
if (n==(Token*)0) n=integer(&r);
if (n==(Token*)0) n=flt(&r);
if (n==(Token*)0) n=ident(&r);
na[j]=n;
while (*r == ' ') ++r;
n = charstring(&r);
if (n == NULLTOKEN) n = op(&r);
if (n == NULLTOKEN) n = integer(&r);
if (n == NULLTOKEN) n = flt(&r);
if (n == NULLTOKEN) n = ident(&r);
na[j] = n;
}
na[j]=(Token*)0;
na[i] = NULLTOKEN;
/*}}}*/
return na;
}
@ -273,20 +276,17 @@ static int print_fident(char* dest, size_t space, int id)
/* printtok -- print a single token, passed by address, although not changed */ /*{{{*/
size_t printtok(char* dest, size_t size, size_t field_width,
int quote_strings, int use_scientific,
int precision, int verbose_error, const Token *tok)
bool quote_strings, bool use_scientific,
int precision, bool verbose_error, const Token *tok)
{
size_t cur;
if (debug_level > 2) {
printf("..Entering printtok; bufsize %d, field_width %d, qs %d, us %d, prec %d, verr %d\n", size, field_width, quote_strings, use_scientific, precision, verbose_error);
}
cur = 0;
if (tok != NULLTOKEN) switch (tok->type)
if (size > 0 ) *dest = '\0';
if (tok == NULLTOKEN || tok->type == EMPTY) return 0;
size_t cur = 0;
switch (tok->type)
{
/* EMPTY */ /*{{{*/
case EMPTY: if (size > 0) dest[cur] = '\0'; break;
/*}}}*/
/* STRING */ /*{{{*/
case STRING:
{
@ -305,11 +305,11 @@ size_t printtok(char* dest, size_t size, size_t field_width,
/* INT */ /*{{{*/
case INT:
{
char buf[20];
char buf[64];
size_t buflen;
buflen=sprintf(buf,"%ld",tok->u.integer);
assert(buflen<sizeof(buf));
buflen = sprintf(buf, INT_T_FMT, tok->u.integer);
assert(buflen < sizeof(buf));
(void)strncpy(dest+cur,buf,size-cur-1);
cur+=buflen;
break;
@ -323,19 +323,22 @@ size_t printtok(char* dest, size_t size, size_t field_width,
size_t len;
/*}}}*/
len=sprintf(buf, use_scientific ? "%.*e" : "%.*f",
precision == -1 ? DBL_DIG-2 : precision,
tok->u.flt);
bool use_sci = use_scientific
|| (int)ABSFLT(LOG10FLT(tok->u.flt)) > FLT_T_DIG;
len = sprintf(buf, use_sci ? FLT_T_SCI_FMT : FLT_T_STD_FMT,
precision == -1 ? FLT_T_DIG-2 : precision,
tok->u.flt);
assert(len<sizeof(buf));
if (!use_scientific && precision==-1)
if (!use_sci)
{
p=&buf[len-1];
while (p>buf && *p=='0' && *(p-1)!='.') { *p='\0'; --p; --len; }
while (p>buf && *p == '0' && *(p-1) != '.') { *p = '\0'; --p; --len; }
}
p=buf+len;
while (*--p==' ') { *p='\0'; --len; }
p = buf+len;
while (*--p == ' ') { *p = '\0'; --len; }
(void)strncpy(dest+cur,buf,size-cur-1);
cur+=len;
cur += len;
break;
}
/*}}}*/

View File

@ -1,17 +1,21 @@
#ifndef SCANNER_H
#define SCANNER_H
#include <math.h>
#include <sys/types.h>
#include <stdbool.h>
#include "func.h"
#ifdef __cplusplus
extern "C" {
#endif
/* TO PRESERVE ABILITY TO READ OLD SAVE FILES, ONLY ADD ITEMS AT END */
typedef enum {
EMPTY
#ifndef __cplusplus
, STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, FUNCALL, EEK
, STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, EEK, FUNCALL
#endif
} Type;
@ -47,26 +51,107 @@ typedef enum { X=0, Y=1, Z=2, HYPER} Dimensions;
bool loc_in_box(const Location test,
const Location b, const Location c);
typedef struct Token_struc Token;
typedef struct
{
int fident;
FunctionIdentifier fident;
int argc;
Token *argv;
} FunctionCall;
#define FLT_LONG_DOUBLE
#ifdef FLT_LONG_DOUBLE
typedef long double FltT;
/* To switch the internal type used to store a floating value, it is
supposed to suffice to redefine all of the following macros:
*/
#define FLT_T_STD_FMT "%.*Lf"
#define FLT_T_SCI_FMT "%.*Le"
#define FLT_T_DIG LDBL_DIG
#define FLTEPS LDBL_EPSILON
#define FLTMX LDBL_MAX
#define STRTOFLT strtold
#define ABSFLT fabsl
#define LOGFLT logl
#define LOG2FLT log2l
#define LOG10FLT log10l
#define SQRTFLT sqrtl
#define POWFLT powl
#define FMODFLT fmodl
#define MODFFLT modfl
#define SINFLT sinl
#define COSFLT cosl
#define TANFLT tanl
#define SINHFLT sinhl
#define COSHFLT coshl
#define TANHFLT tanhl
#define ASINFLT asinl
#define ACOSFLT acosl
#define ATANFLT atanl
#define FLOORFLT floorl
#define CEILFLT ceill
#define ROUNDFLT rintl
#define TRUNCFLT truncl
#else
typedef double FltT;
/* To switch the internal type used to store a floating value, it is
supposed to suffice to redefine all of the following macros:
*/
#define FLT_T_STD_FMT "%.*f"
#define FLT_T_SCI_FMT "%.*e"
#define FLT_T_DIG DBL_DIG
#define FLTEPS DBL_EPSILON
#define FLTMX DBL_MAX
#define STRTOFLT strtod
#define ABSFLT fabs
#define LOGFLT log
#define LOG2FLT log2
#define LOG10FLT log10
#define SQRTFLT sqrt
#define POWFLT pow
#define FMODFLT fmod
#define MODFFLT modf
#define SINFLT sin
#define COSFLT cos
#define TANFLT tan
#define SINHFLT sinh
#define COSHFLT cosh
#define TANHFLT tanh
#define ASINFLT asin
#define ACOSFLT acos
#define ATANFLT atan
#define FLOORFLT floor
#define CEILFLT ceil
#define ROUNDFLT rint
#define TRUNCFLT trunc
#endif
typedef long long IntT;
/* To switch the internal type used to store an integer value, it is
supposed to suffice to redefine all of the following macros and typedefs.
*/
typedef unsigned long long UIntT;
#define INT_T_FMT "%lld"
#define STRTOINT strtoll
/* This one depends on both the floating point and integer types */
#ifdef FLT_LONG_DOUBLE
#define ROUNDFLTINT llrintl
#else
#define ROUNDFLTINT llrint
#endif
/* End of subtype definitions */
/* The main type defined here; should this be token.h ?*/
typedef struct Token_struc
{
Type type;
union
{
char *string;
double flt;
long integer;
FltT flt;
IntT integer;
Operator op;
char *lident;
int fident;
FunctionIdentifier fident;
Location location;
FunctionCall funcall;
char *err;
@ -75,13 +160,14 @@ typedef struct Token_struc
#define NULLTOKEN ((Token*)0)
#define EMPTY_TVEC ((Token**)0)
#define TOKISNUM(t) ((t).type == INT || (t).type == FLOAT || (t).type == EMPTY)
int identcode(const char *s, size_t len);
void duperror(Token* tok, const char* erro);
Token duperror(Token* tok, const char* erro);
Token **scan(const char **s);
void cleartoken(Token* tok);
size_t printtok(char *dest, size_t size, size_t field_width,
int quote_strings, int use_scientific,
int precision, int verbose_error, const Token *tok);
bool quote_strings, bool use_scientific,
int precision, bool verbose_error, const Token *tok);
void print(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Token **n);
#ifdef __cplusplus

View File

@ -46,7 +46,8 @@ int max_eval;
/*}}}*/
/* copycelltosheet -- copy a cell into a sheet*/ /*{{{*/
void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to)
void copycelltosheet(const Cell *fromcell, Sheet *sheet2,
const Location to, LabelHandling lh)
{
assert(sheet2 != (Sheet*)0);
Cell *tocell = safe_cell_at(sheet2, to);
@ -59,7 +60,7 @@ void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to)
sheet2->changed = 1;
Cell *alsotocell = initcellofsheet(sheet2, to);
assert(tocell == NULLCELL || tocell == alsotocell);
copycell(alsotocell, fromcell);
copycell(alsotocell, fromcell, lh);
}
/*}}}*/
}
@ -91,16 +92,14 @@ static void dump_cell(Sheet *sheet, int x, int y, int z)
return;
}
printf("TEADUMP of &(%d,%d,%d):\n", x, y, z);
print(buf, sizeof(buf), 0, 1, 0, -1, c->contents[BASE]);
printf(" Base expr: %s.\n", buf);
print(buf, sizeof(buf), 0, 1, 0, -1, c->contents[ITERATIVE]);
printf(" Update expr: %s.\n", buf);
if (c->label == (char *)0) printf("\n No label.\n");
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
{
printf(" %s: ", TokVariety_Name[tv]);
printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, c->tok + tv);
printf("%s.\n", buf);
}
if (c->label == NULL) printf("\n No label.\n");
else printf("\n Label: %s.\n", c->label);
printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, &(c->value));
printf(" Current value: %s.\n", buf);
printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, &(c->resvalue));
printf(" Stored result value: %s.\n Adjustment: ", buf);
switch (c->adjust) {
case LEFT: printf("LEFT\n"); break;
case RIGHT: printf("RIGHT\n"); break;
@ -108,6 +107,8 @@ static void dump_cell(Sheet *sheet, int x, int y, int z)
case AUTOADJUST: printf("AUTO\n"); break;
}
printf(" Precision: %d\n Attributes: ", c->precision);
for (ColorAspect ca = FOREGROUND; ca < NUM_COLOR_ASPECTS; ++ca)
printf(" %s color: %d\n", ColorAspect_Name[ca], c->aspect[ca]);
if (c->updated) printf("updated ");
if (c->shadowed) printf("shadowed ");
if (c->scientific) printf("scientific ");
@ -154,6 +155,30 @@ static void swapblock(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int
sheet2->changed=1;
}
/*}}}*/
/* signum -- -1 if < 0, 0 if 0, 1 if > 0 */
static int signum(int x) {
return (x > 0) - (x < 0);
}
/* cmpflt -- -1 if <, 0 if =, 1 if > */
static int cmpflt(FltT x, FltT y) {
return (x > y) - (x < y);
}
/* cmpflint -- -1 if <, 0 if =, 1 if > */
static int cmpflint(FltT x, IntT y) {
return (x > y) - (x < y);
}
/* cmpint -- -1 if <, 0 if =, 1 if > */
static int cmpint(IntT x, IntT y) {
return (x > y) - (x < y);
}
/* cmpintfl -- -1 if <, 0 if =, 1 if > */
static int cmpintfl(IntT x, FltT y) {
return (x > y) - (x < y);
}
#define NOT_COMPARABLE 2
/* cmpcell -- compare to cells with given order flags */ /*{{{*/
/* Notes */ /*{{{*/
/*
@ -164,73 +189,42 @@ second. A result of 2 means they are not comparable.
/*}}}*/
static int cmpcell(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2, int sortkey)
{
Cell *leftcell, *rightcell;
assert(sheet1!=(Sheet*)0);
assert(sheet2!=(Sheet*)0);
int ascending = (sortkey & ASCENDING) ? 1 : -1;
Token leftval = gettok(safe_cell_atc(sheet1, x1, y1, z1), CURR_VAL);
Token rightval = gettok(safe_cell_atc(sheet2, x2, y2, z2), CURR_VAL);
/* empty cells are smaller than any non-empty cell */ /*{{{*/
if (!CELL_IS_GOODC(sheet1,x1,y1,z1) || CELL_ATC(sheet1,x1,y1,z1)->value.type==EMPTY)
{
if (!CELL_IS_GOODC(sheet2,x2,y2,z2) || CELL_ATC(sheet2,x2,y2,z2)->value.type==EMPTY) return 0;
else return (sortkey&ASCENDING ? -1 : 1);
}
if (!CELL_IS_GOODC(sheet2,x2,y2,z2) || CELL_ATC(sheet2,x2,y2,z2)->value.type==EMPTY) return (sortkey&ASCENDING ? 1 : -1);
if (leftval.type == EMPTY)
if (rightval.type == EMPTY) return 0;
else return -ascending;
if (rightval.type == EMPTY) return ascending;
/*}}}*/
leftcell = CELL_ATC(sheet1,x1,y1,z1);
rightcell = CELL_ATC(sheet2,x2,y2,z2);
switch (leftcell->value.type)
switch (leftval.type)
{
/* STRING */ /*{{{*/
case STRING:
{
if (rightcell->value.type==STRING)
{
int r;
r=strcmp(leftcell->value.u.string, rightcell->value.u.string);
if (r<0) return (sortkey&ASCENDING ? -1 : 1);
else if (r==0) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
return 2;
}
if (rightval.type == STRING)
return signum(strcmp(leftval.u.string, rightval.u.string)) * ascending;
return NOT_COMPARABLE;
/*}}}*/
/* FLOAT */ /*{{{*/
case FLOAT:
{
if (rightcell->value.type==FLOAT)
{
if (leftcell->value.u.flt<rightcell->value.u.flt) return (sortkey&ASCENDING ? -1 : 1);
else if (leftcell->value.u.flt==rightcell->value.u.flt) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
if (rightcell->value.type==INT)
{
if (leftcell->value.u.flt<rightcell->value.u.integer) return (sortkey&ASCENDING ? -1 : 1);
else if (leftcell->value.u.flt==rightcell->value.u.integer) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
return 2;
}
if (rightval.type == FLOAT)
return cmpflt(leftval.u.flt, rightval.u.flt)*ascending;
if (rightval.type == INT)
return cmpflint(leftval.u.flt, rightval.u.integer)*ascending;
return NOT_COMPARABLE;
/*}}}*/
/* INT */ /*{{{*/
case INT:
{
if (rightcell->value.type==INT)
{
if (leftcell->value.u.integer<rightcell->value.u.integer) return (sortkey&ASCENDING ? -1 : 1);
else if (leftcell->value.u.integer==rightcell->value.u.integer) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
if (rightcell->value.type==FLOAT)
{
if (leftcell->value.u.integer<rightcell->value.u.flt) return (sortkey&ASCENDING ? -1 : 1);
else if (leftcell->value.u.integer==rightcell->value.u.flt) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
return 2;
if (rightval.type == INT)
return cmpint(leftval.u.integer, rightval.u.integer)*ascending;
if (rightval.type == FLOAT)
return cmpintfl(leftval.u.integer, rightval.u.flt)*ascending;
return NOT_COMPARABLE;
}
/*}}}*/
default: return 2;
default: return NOT_COMPARABLE;
}
}
/*}}}*/
@ -334,6 +328,17 @@ Cell *initcellofsheet(Sheet *sheet, const Location at)
even in the face of various edge conditions.
*/
Cell *safe_cell_atc(Sheet *sheet, int x, int y, int z)
{
if (sheet == (Sheet*)0) return NULLCELL;
if (sheet->sheet == (Cell **)0) return NULLCELL;
if (!LOC_WITHINC(sheet,x,y,z)) return NULLCELL;
return CELL_ATC(sheet,x,y,z);
}
/* safe_cell_at -- returns pointer to the cell at the given location,
even in the face of various edge conditions.
*/
Cell *safe_cell_at(Sheet *sheet, const Location at)
{
if (sheet == (Sheet*)0) return NULLCELL;
@ -511,21 +516,21 @@ int cellwidth(Sheet *sheet, const Location at)
/*}}}*/
/* putcont -- assign new contents */ /*{{{*/
void putcont(Sheet *sheet, const Location at, Token **t, ContentVariety v)
void puttok(Sheet *sheet, const Location at, Token t, TokVariety v)
{
Cell *cell;
assert(sheet != (Sheet*)0);
sheet->changed = 1;
cell = initcellofsheet(sheet, at);
tvecfree(cell->contents[v]);
cell->contents[v] = t;
tfree(&(cell->tok[v]));
cell->tok[v] = t;
redraw_cell(sheet, at);
}
/*}}}*/
/* getvalue -- get tcopy()ed value */ /*{{{*/
Token getvalue(Sheet *sheet, const Location at)
/* recompvalue -- get tcopy()ed value */ /*{{{*/
Token recompvalue(Sheet *sheet, const Location at)
{
/* variables */ /*{{{*/
Token result;
@ -551,19 +556,13 @@ Token getvalue(Sheet *sheet, const Location at)
depending on this call to update the current value
*/
cell = CELL_AT(sheet, at);
if (!upd_clock && getcont(cell,2) == EMPTY_TVEC) return result;
if (!upd_clock && gettok(cell, CONTINGENT).type == EMPTY) return result;
/* update value of this cell if needed and return it */ /*{{{*/
orig_upd_clock = upd_clock;
if (cell->ignored)
{
/* variables */ /*{{{*/
Token oldvalue;
/*}}}*/
oldvalue = cell->value;
tfree(cell->tok + CURR_VAL);
cell->updated = 1;
cell->value.type = EMPTY;
tfree(&oldvalue);
}
else if (cell->updated == 0)
{
@ -583,24 +582,24 @@ Token getvalue(Sheet *sheet, const Location at)
if (cell->clock_t1 == 0)
{
cell->updated = 1;
oldvalue = cell->value;
oldvalue = gettok(cell, CURR_VAL);
upd_clock = 0;
cell->value = eval_safe(getcont(cell, CONTINGENT), FULL);
cell->tok[CURR_VAL] = evaltoken(gettok(cell, CONTINGENT), FULL);
tfree(&oldvalue);
}
else if (upd_clock)
{
cell->updated = 1;
upd_clock = 0;
oldvalue = cell->resvalue;
cell->resvalue = eval_safe(getcont(cell, CONTINGENT), FULL);
oldvalue = gettok(cell, RES_VAL);
cell->tok[RES_VAL] = evaltoken(gettok(cell, CONTINGENT), FULL);
tfree(&oldvalue);
}
upd_sheet = old_sheet;
LOCATION_GETS(upd_l, old_l);
max_eval = old_max_eval;
}
if (!orig_upd_clock) result = tcopy(cell->value);
if (!orig_upd_clock) result = tcopy(cell->tok[CURR_VAL]);
/*}}}*/
return result;
}
@ -638,15 +637,15 @@ void update(Sheet *sheet)
for (ALL_LOCS_IN_SHEET(sheet,w))
{
upd_clock = 1;
getvalue(sheet, w);
recompvalue(sheet, w);
}
for (ALL_CELLS_IN_SHEET(sheet,i,cell))
{
if (cell && cell->clock_t1)
{
tfree(&(cell->value));
cell->value = cell->resvalue;
cell->resvalue.type = EMPTY;
tfree(&(cell->tok[CURR_VAL]));
cell->tok[CURR_VAL] = cell->tok[RES_VAL];;
cell->tok[RES_VAL].type = EMPTY;
cell->clock_t1 = 0;
}
}
@ -663,25 +662,20 @@ char *geterror(Sheet *sheet, const Location at)
Token v;
assert(sheet!=(Sheet*)0);
if ((v = getvalue(sheet,at)).type != EEK)
if ((v = recompvalue(sheet,at)).type != EEK)
{
tfree(&v);
return (char*)0;
}
else
{
return (v.u.err);
return NULL;
}
else return (v.u.err);
}
/*}}}*/
/* printvalue -- get ASCII representation of value */ /*{{{*/
void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, const Location at)
{
Token t;
assert(sheet != (Sheet*)0);
t = getvalue(sheet, at);
Token t = recompvalue(sheet, at);
printtok(s, size, chars, quote, scientific, precision, 0, &t);
tfree(&t);
}
@ -842,91 +836,46 @@ Token findlabel(Sheet *sheet, const char *label)
}
/*}}}*/
/* search and replace for labels in a token */
static void relabel_token(Token *tok, const char *oldl, const char *newl)
{
switch (tok->type)
{
case LIDENT:
if (strcmp(tok->u.lident, oldl) == 0)
{
tfree(tok);
tok->type = LIDENT;
tok->u.lident = strdup(newl);
}
break;
case FUNCALL:
for (int ai = 0; ai < tok->u.funcall.argc; ++ai)
relabel_token((tok->u.funcall.argv) + ai, oldl, newl);
break;
default:
break;
}
}
/* relabel -- search and replace for labels */ /*{{{*/
void relabel(Sheet *sheet, const Location at,
const char *oldlabel, const char *newlabel)
{
/* variables */ /*{{{*/
Token **run;
ContentVariety v;
Cell *cell;
/*}}}*/
/* asserts */ /*{{{*/
assert(oldlabel!=(const char*)0);
assert(newlabel!=(const char*)0);
/*}}}*/
if (!LOC_WITHIN(sheet, at)) return;
cell = CELL_AT(sheet, at);
Cell *cell = CELL_AT(sheet, at);
if (cell != NULLCELL)
for (v = BASE; v <= ITERATIVE; ++v)
if (cell->contents[v] != EMPTY_TVEC)
for (run = cell->contents[v]; *run != NULLTOKEN; ++run)
if ((*run)->type==LIDENT && strcmp((*run)->u.lident, oldlabel)==0)
{
free((*run)->u.lident);
(*run)->u.lident = strdup(newlabel);
}
cachelabels(sheet);
forceupdate(sheet);
for (TokVariety v = BASE_CONT; v <= MAX_PERSIST_TV; ++v)
relabel_token(cell->tok + v, oldlabel, newlabel);
}
/*}}}*/
/* savexdr -- save a spread sheet in XDR */ /*{{{*/
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count)
{
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
int x,y,z;
/*}}}*/
*count=0;
if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_ENCODE);
if (!xdr_magic(&xdrs))
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
for (x=sheet->dimx-1; x>=0; --x) for (z=sheet->dimz-1; z>=0; --z)
{
int width;
int u;
width=columnwidth(sheet,x,z);
if (width!=DEF_COLUMNWIDTH)
{
u=0;
if (xdr_int(&xdrs,&u)==0 || xdr_column(&xdrs,&x,&z,&width)==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
}
for (y=sheet->dimy-1; y>=0; --y)
{
if (CELL_ATC(sheet,x,y,z)!=NULLCELL)
{
u=1;
if (xdr_int(&xdrs,&u)==0 || xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0 || xdr_cell(&xdrs,CELL_ATC(sheet,x,y,z))==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
++*count;
}
}
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
return (const char*)0;
}
/*}}}*/
/* savetbl -- save as tbl tyble */ /*{{{*/
const char *savetbl(Sheet *sheet, const char *name, int body,
const Location beg, const Location end,
@ -1131,73 +1080,6 @@ const char *savecsv(Sheet *sheet, const char *name, char sep,
/* variables */ /*{{{*/
FILE *fp;
Location w;
Cell *cw;
/*}}}*/
/* asserts */ /*{{{*/
assert(sheet!=(Sheet*)0);
assert(name!=(const char*)0);
/*}}}*/
*count=0;
w[X] = beg[X];
for (w[Z]=beg[Z]; w[Z]<=end[Z]; ++(w[Z]))
for (w[Y]=beg[Y]; w[Y]<=end[Y]; ++(w[Y]))
if (shadowed(CELL_AT(sheet,w))) return _("Shadowed cells in first column");
if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
for (w[Z]=beg[Z]; w[Z]<=end[Z]; ++(w[Z]))
{
for (w[Y]=beg[Y]; w[Y]<=end[Y]; ++(w[Y]))
{
for (w[X]=beg[X]; w[X]<=end[X]; ++(w[X]))
{
if (w[X]>beg[X]) if (fputc_close(sep,fp)==EOF) return strerror(errno);
cw = CELL_AT(sheet,w);
if (cw != NULLCELL)
{
char *buf,*s;
buf=malloc(255*UTF8SZ+1);
printvalue(buf, 255*UTF8SZ+1, 255, 0, getscientific(cw),
getprecision(cw), sheet, w);
if (cw->value.type == STRING && fputc_close('"',fp)==EOF)
{
free(buf);
return strerror(errno);
}
for (s=buf; *s; ++s)
{
if (fputc_close(*s,fp)==EOF || (*s=='"' && fputc_close(*s,fp)==EOF))
{
free(buf);
return strerror(errno);
}
}
free(buf);
if (cw->value.type==STRING && fputc_close('"',fp)==EOF) return strerror(errno);
}
++*count;
}
if (fputc_close('\n',fp)==EOF) return strerror(errno);
}
if (w[Z] < end[Z] && fputs_close("\f",fp)==EOF) return strerror(errno);
}
if (fclose(fp)==EOF) return strerror(errno);
return (const char*)0;
}
/*}}}*/
static const char *saveport_contentleader[] =
{ [BASE] = ":", [ITERATIVE] = "\\\n"
};
/* saveport -- save as portable text */ /*{{{*/
const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
{
/* variables */ /*{{{*/
FILE *fp;
int x,y,z;
Cell *cell;
ContentVariety v;
/*}}}*/
/* asserts */ /*{{{*/
@ -1205,17 +1087,84 @@ const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
assert(name != (const char*)0);
/*}}}*/
*count = 0;
if ((fp=fopen(name,"w")) == (FILE*)0) return strerror(errno);
fprintf(fp,"# This is a work sheet generated with teapot %s.\n",VERSION);
for (z=sheet->dimz-1; z>=0; --z)
w[X] = beg[X];
for (w[Z] = beg[Z]; w[Z] <= end[Z]; ++(w[Z]))
for (w[Y] = beg[Y]; w[Y] <= end[Y]; ++(w[Y]))
if (shadowed(CELL_AT(sheet,w))) return _("Shadowed cells in first column");
if ((fp = fopen(name,"w")) == (FILE*)0) return strerror(errno);
for (w[Z]=beg[Z]; w[Z]<=end[Z]; ++(w[Z]))
{
for (y=sheet->dimy-1; y>=0; --y)
for (w[Y]=beg[Y]; w[Y]<=end[Y]; ++(w[Y]))
{
for (x=sheet->dimx-1; x>=0; --x)
for (w[X]=beg[X]; w[X]<=end[X]; ++(w[X]))
{
if (w[X] > beg[X])
if (fputc_close(sep,fp) == EOF) return strerror(errno);
Cell *cw = CELL_AT(sheet,w);
if (cw != NULLCELL)
{
static const int bufsz = 511*UTF8SZ + 1;
char *buf = malloc(bufsz);
printvalue(buf, bufsz, 255, 0, getscientific(cw),
getprecision(cw), sheet, w);
bool needquote = !transparent(cw)
&& (strlen(buf) != strcspn(buf, "\",\n\r"));
if (needquote && fputc_close('"',fp) == EOF)
{
free(buf);
return strerror(errno);
}
for (const char *s = buf; *s; ++s)
{
if (fputc_close(*s,fp) == EOF
|| (*s=='"' && needquote && fputc_close(*s,fp) == EOF))
{
free(buf);
return strerror(errno);
}
}
free(buf);
if (needquote && fputc_close('"', fp) == EOF) return strerror(errno);
}
++*count;
}
if (fputc_close('\n', fp) == EOF) return strerror(errno);
}
if (w[Z] < end[Z] && fputc_close('\f', fp) == EOF) return strerror(errno);
}
if (fclose(fp) == EOF) return strerror(errno);
return (const char*)0;
}
/*}}}*/
static const char *saveport_contentleader[] =
{ [BASE_CONT] = ":", [ITER_CONT] = "\\\n", [ATTR_REF] = "\\\n"
};
/* saveport -- save as portable text */ /*{{{*/
const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
{
/* variables */ /*{{{*/
FILE *fp;
/*}}}*/
/* asserts */ /*{{{*/
assert(sheet != (Sheet*)0);
assert(name != (const char*)0);
/*}}}*/
*count = 0;
if ((fp = fopen(name,"w")) == (FILE*)0) return strerror(errno);
fprintf(fp,"# This is a work sheet generated with teapot %s.\n",VERSION);
for (int z = sheet->dimz - 1; z >= 0; --z)
{
for (int y = sheet->dimy - 1; y >= 0; --y)
{
for (int x = sheet->dimx - 1; x >= 0; --x)
{
if (y == 0 && columnwidth(sheet,x,z) != DEF_COLUMNWIDTH)
fprintf(fp,"W%d %d %d\n",x,z,columnwidth(sheet,x,z));
cell = CELL_ATC(sheet,x,y,z);
Cell *cell = CELL_ATC(sheet,x,y,z);
if (cell != NULLCELL)
{
fprintf(fp,"C%d %d %d ",x,y,z);
@ -1224,23 +1173,36 @@ const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
if (cell->label) fprintf(fp,"L%s ", cell->label);
if (cell->precision != -1)
fprintf(fp,"P%d ", cell->precision);
bool needscolor = false;
for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a)
if (cell->aspect[a] != DefaultCN[a])
{ needscolor = true; break; }
if (needscolor)
{
fprintf(fp, "H%d ", NUM_COLOR_ASPECTS);
for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a)
fprintf(fp, "%d ", (int)cell->aspect[a]);
}
if (cell->shadowed) fprintf(fp,"S ");
if (cell->bold) fprintf(fp,"B ");
if (cell->underline) fprintf(fp,"U ");
if (cell->scientific != DEF_SCIENTIFIC) fprintf(fp,"E ");
if (cell->locked) fprintf(fp,"C ");
if (cell->locked) fprintf(fp, "K ");
if (cell->transparent) fprintf(fp,"T ");
for (v = BASE; v <= ITERATIVE; ++v)
if (cell->contents[v])
{
char buf[4096];
if (fputs_close(saveport_contentleader[v], fp) == EOF)
return strerror(errno);
print(buf, sizeof(buf), 0, 1, cell->scientific, cell->precision,
cell->contents[v]);
if (fputs_close(buf, fp) == EOF) return strerror(errno);
}
if (cell->ignored) fprintf(fp, "I ");
TokVariety lastv = MAX_PERSIST_TV;
while (lastv > BASE_CONT && cell->tok[lastv].type == EMPTY) --lastv;
for (TokVariety v = BASE_CONT; v <= lastv; ++v)
{
if (v == BASE_CONT && cell->tok[v].type == EMPTY) continue;
char buf[4096];
if (fputs_close(saveport_contentleader[v], fp) == EOF)
return strerror(errno);
printtok(buf, sizeof(buf), 0, 1, cell->scientific,
cell->precision, 0, cell->tok + v);
if (fputs_close(buf, fp) == EOF) return strerror(errno);
}
if (fputc_close('\n', fp) == EOF) return strerror(errno);
}
}
}
@ -1274,16 +1236,16 @@ const char *loadport(Sheet *sheet, const char *name)
{
/* remove nl */ /*{{{*/
width = strlen(buf);
if (width>0 && buf[width-1]=='\n') buf[--width] = '\0';
if (width > 0 && buf[width-1] == '\n') buf[--width] = '\0';
/*}}}*/
switch (buf[0])
{
/* C -- parse cell */ /*{{{*/
case 'C':
{
ContentVariety cv = BASE, nextcv = BASE;
TokVariety rv = BASE_CONT, nextrv = BASE_CONT;
if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextcv; }
if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextrv; }
initcellcontents(&loaded);
/* parse x y and z */ /*{{{*/
os=ns=buf+1;
@ -1306,7 +1268,7 @@ const char *loadport(Sheet *sheet, const char *name)
while (*ns==' ') ++ns;
os=ns;
loc[Z] = posnumber(os,&ns);
if (os==ns)
if (os == ns)
{
sprintf(errbuf,_("Parse error for z position in line %d"),line);
err=errbuf;
@ -1360,6 +1322,34 @@ const char *loadport(Sheet *sheet, const char *name)
break;
}
/*}}}*/
/* H -- hue (colors) */ /*{{{*/
case 'H':
{
os = ++ns;
size_t na = posnumber(os, &ns);
if (os == ns || na > NUM_COLOR_ASPECTS)
{
sprintf(errbuf, _("Parse error for number of hues in line %d"),
line);
err = errbuf;
goto eek;
}
for (size_t a = 0; a < na; ++a)
{
while (*ns && (*ns == ' ')) { ++ns; }
os = ns;
loaded.aspect[a] = posnumber(os, &ns);
if (os == ns)
{
sprintf(errbuf, _("Parse error in hue %d on line %d"),
a, line);
err = errbuf;
goto eek;
}
}
break;
}
/*}}}*/
/* S -- shadowed */ /*{{{*/
case 'S':
{
@ -1413,8 +1403,9 @@ const char *loadport(Sheet *sheet, const char *name)
break;
}
/*}}}*/
/* O -- locked */ /*{{{*/
case 'O':
/* K (formerly C) -- locked */ /*{{{*/
case 'C':
case 'K':
{
++ns;
loaded.locked=1;
@ -1451,26 +1442,37 @@ const char *loadport(Sheet *sheet, const char *name)
/* convert remaining string(s) into token sequence */ /*{{{*/
while (true)
{
loaded.contents[cv] = scan(&ns);
if (loaded.contents[cv]==(Token**)0)
Token **t = scan(&ns);
if (t == EMPTY_TVEC)
{
sprintf(errbuf,_("Expression syntax error in line %d"),line);
err=errbuf;
goto eek;
}
if (nextcv == cv) break;
cv = nextcv;
Token pt = eval_safe(t, LITERAL);
tvecfree(t);
if (pt.type == EEK)
{
sprintf(errbuf, _("Parse error in line %d: %s"),
line, pt.u.err);
err = errbuf;
tfree(&pt);
goto eek;
}
loaded.tok[rv] = pt;
if (nextrv == rv) break;
rv = nextrv;
if (fgets(buf, sizeof(buf), fp) == (char*)0) break;
++line;
width = strlen(buf);
if (width>0 && buf[width-1]=='\n') buf[width-1]='\0';
/* More content? */
if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextcv; }
if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextrv; }
ns = buf;
}
/*}}}*/
copycelltosheet(&loaded, sheet, loc);
copycelltosheet(&loaded, sheet, loc, PRESERVE_LABEL);
break;
}
/*}}}*/
@ -1534,96 +1536,6 @@ const char *loadport(Sheet *sheet, const char *name)
}
/*}}}*/
/* loadxdr -- load a spread sheet in XDR */ /*{{{*/
const char *loadxdr(Sheet *sheet, const char *name)
{
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
Location w;
int width;
int u;
int olderror;
Cell *nc;
/*}}}*/
if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
if (!xdr_magic(&xdrs))
{
#if 0
xdr_destroy(&xdrs);
fclose(fp);
return _("This is not a teapot worksheet in XDR format");
#else
xdr_destroy(&xdrs);
rewind(fp);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
#endif
}
freesheet(sheet,0);
while (xdr_int(&xdrs,&u)) switch (u)
{
/* 0 -- column width element */ /*{{{*/
case 0:
{
if (xdr_column(&xdrs,&(w[X]),&(w[Z]),&width)==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
setwidth(sheet,w[X],w[Z],width);
break;
}
/*}}}*/
/* 1 -- cell element */ /*{{{*/
case 1:
{
if (xdr_int(&xdrs,&(w[X]))==0 ||
xdr_int(&xdrs,&(w[Y]))==0 ||
xdr_int(&xdrs,&(w[Z]))==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
nc = initcellofsheet(sheet, w);
if (xdr_cell(&xdrs, nc)==0)
{
freecellofsheet(sheet, w);
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
break;
}
/*}}}*/
/* default -- should not happen */ /*{{{*/
default:
{
xdr_destroy(&xdrs);
fclose(fp);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
return _("Invalid record, loading aborted");
}
/*}}}*/
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
redraw_sheet(sheet);
return (const char*)0;
}
/*}}}*/
/* loadcsv -- load/merge CSVs */ /*{{{*/
const char *loadcsv(Sheet *sheet, const char *name)
{
@ -1634,8 +1546,8 @@ const char *loadcsv(Sheet *sheet, const char *name)
Location where;
char ln[4096];
const char *str;
double value;
long lvalue;
FltT value;
IntT lvalue;
int separator = 0;
/*}}}*/
@ -1661,51 +1573,50 @@ const char *loadcsv(Sheet *sheet, const char *name)
where[X] = sheet->cur[X];
do
{
t=malloc(2*sizeof(Token*));
t[0]=malloc(sizeof(Token));
t[1]=(Token*)0;
lvalue=csv_long(s,&cend);
if (s!=cend) /* ok, it is a integer */ /*{{{*/
Token t;
t.type = EMPTY;
lvalue = csv_long(s, &cend);
if (s != cend) /* ok, it is a integer */ /*{{{*/
{
t[0]->type=INT;
t[0]->u.integer=lvalue;
putcont(sheet, where, t, BASE);
t.type = INT;
t.u.integer = lvalue;
puttok(sheet, where, t, BASE_CONT);
}
/*}}}*/
else
{
value=csv_double(s,&cend);
if (s!=cend) /* ok, it is a double */ /*{{{*/
value = csv_double(s, &cend);
if (s != cend) /* ok, it is a double */ /*{{{*/
{
t[0]->type=FLOAT;
t[0]->u.flt=value;
putcont(sheet, where, t, BASE);
t.type = FLOAT;
t.u.flt = value;
puttok(sheet, where, t, BASE_CONT);
}
/*}}}*/
else
{
str=csv_string(s,&cend);
if (s!=cend) /* ok, it is a string */ /*{{{*/
str = csv_string(s, &cend);
if (s != cend) /* ok, it is a string */ /*{{{*/
{
t[0]->type=STRING;
t[0]->u.string=strdup(str);
putcont(sheet, where, t, BASE);
t.type = STRING;
t.u.string = strdup(str);
puttok(sheet, where, t, BASE_CONT);
}
/*}}}*/
else
{
tvecfree(t);
csv_separator(s,&cend);
while (s==cend && *s && *s!='\n')
while (s == cend && *s && *s!='\n')
{
err=_("unknown values ignored");
csv_separator(++s,&cend);
csv_separator(++s, &cend);
}
/* else it is nothing, which does not need to be stored :) */
}
}
}
} while (s!=cend ? s=cend,++(where[X]),1 : 0);
} while (s != cend ? s = cend, ++(where[X]), 1 : 0);
}
fclose(fp);
return err;
@ -1884,7 +1795,8 @@ void moveblock(Sheet *sheet, const Location beg, const Location end,
get[X] != to[X]; get[X] += dir[X], go[X] += dir[X])
{
if (!LOC_WITHIN(sheet, get)) freecellofsheet(sheet, go);
else if (copy) { copycelltosheet(CELL_AT(sheet, get), sheet, go); }
else if (copy)
copycelltosheet(CELL_AT(sheet, get), sheet, go, ALTER_LABEL);
else
{
resize(sheet, go[X], go[Y], go[Z]);
@ -1916,7 +1828,8 @@ const char *sortblock(Sheet *sheet, const Location beg, const Location end,
int x,y,z;
int incx=0,incy=0,incz=0;
int distx,disty,distz;
int i,r=-3,norel,work;
int i,r=-3,work;
bool norel = false;
/*}}}*/
/* unpack corners */
@ -1954,8 +1867,8 @@ const char *sortblock(Sheet *sheet, const Location beg, const Location end,
{
for (i=0; i<sklen; ++i)
{
r=cmpcell(sheet,x+sk[i].x,y+sk[i].y,z+sk[i].z,sheet,x+incx+sk[i].x,y+incy+sk[i].y,z+incz+sk[i].z,sk[i].sortkey);
if (r==2) norel=1;
r = cmpcell(sheet,x+sk[i].x,y+sk[i].y,z+sk[i].z,sheet,x+incx+sk[i].x,y+incy+sk[i].y,z+incz+sk[i].z,sk[i].sortkey);
if (r == NOT_COMPARABLE) norel = true;
else if (r==-1 || r==1) break;
else assert(r==0);
}

View File

@ -75,8 +75,10 @@ extern int max_eval;
void initialize_sheet(Sheet *sheet);
void resize(Sheet *sheet, int x, int y, int z);
Cell *initcellofsheet(Sheet *sheet, const Location at);
void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to);
void copycelltosheet(const Cell *fromcell, Sheet *sheet2,
const Location to, LabelHandling lh);
Cell *safe_cell_at(Sheet *sheet, const Location at);
Cell *safe_cell_atc(Sheet *sheet, int x, int y, int z);
Cell *curcell(Sheet *sheet);
void cachelabels(Sheet *sheet);
void freesheet(Sheet *sheet, int all);
@ -87,8 +89,8 @@ void freecellofsheet(Sheet *sheet, const Location at);
int columnwidth(Sheet *sheet, int x, int z);
void setwidth(Sheet *sheet, int x, int z, int width);
int cellwidth(Sheet *sheet, const Location at);
void putcont(Sheet *sheet, const Location at, Token **t, ContentVariety v);
Token getvalue(Sheet *sheet, const Location at);
void puttok(Sheet *sheet, const Location at, Token t, TokVariety v);
Token recompvalue(Sheet *sheet, const Location at);
void update(Sheet *sheet);
char *geterror(Sheet *sheet, const Location at);
void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, const Location at);
@ -107,12 +109,10 @@ Token findlabel(Sheet *sheet, const char *label);
void relabel(Sheet* sheet, const Location at,
const char *oldlabel, const char *newlabel);
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count);
const char *savetbl(Sheet *sheet, const char *name, int body, const Location beg, const Location end, unsigned int *count);
const char *savetext(Sheet *sheet, const char *name, const Location beg, const Location end, unsigned int *count);
const char *savecsv(Sheet *sheet, const char *name, char sep, const Location beg, const Location end, unsigned int *count);
const char *saveport(Sheet *sheet, const char *name, unsigned int *count);
const char *loadxdr(Sheet *sheet, const char *name);
const char *loadport(Sheet *sheet, const char *name);
const char *loadcsv(Sheet *sheet, const char *name);

View File

@ -24,6 +24,7 @@ extern char *strdup(const char *s);
#include "eval.h"
#include "parser.h"
#include "main.h"
#include "misc.h"
#include "sheet.h"
@ -50,9 +51,9 @@ static int it(const char *s)
}
/*}}}*/
/* dbl -- convert string to double */ /*{{{*/
static double dbl(const unsigned char *s)
static FltT dbl(const unsigned char *s)
{
double x;
FltT x;
int sg,e,i;
x=0.0;
@ -80,6 +81,7 @@ static double dbl(const unsigned char *s)
return (sg*ldexp(x,e));
}
/*}}}*/
/* format -- convert string into format */ /*{{{*/
static void format(unsigned char s, Cell *cell)
{
@ -192,12 +194,12 @@ static int pair(const char *s, Token **t, int *tokens)
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+2]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FIDENT;
t[*tokens]->u.fident=identcode("x",1);
t[*tokens+1]->type=OPERATOR;
t[*tokens+1]->u.op=OP;
t[*tokens+2]->type=OPERATOR;
t[*tokens+2]->u.op=CP;
t[*tokens]->type = FIDENT;
t[*tokens]->u.fident = FUNC_X;
t[*tokens+1]->type = OPERATOR;
t[*tokens+1]->u.op = OP;
t[*tokens+2]->type = OPERATOR;
t[*tokens+2]->u.op = CP;
#if WK1DEBUG
fprintf(se,"x()");
#endif
@ -269,12 +271,12 @@ static int pair(const char *s, Token **t, int *tokens)
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+2]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FIDENT;
t[*tokens]->u.fident=identcode("y",1);
t[*tokens+1]->type=OPERATOR;
t[*tokens+1]->u.op=OP;
t[*tokens+2]->type=OPERATOR;
t[*tokens+2]->u.op=CP;
t[*tokens]->type = FIDENT;
t[*tokens]->u.fident = FUNC_Y;
t[*tokens+1]->type = OPERATOR;
t[*tokens+1]->u.op = OP;
t[*tokens+2]->type = OPERATOR;
t[*tokens+2]->u.op = CP;
#if WK1DEBUG
fprintf(se,"y()");
#endif
@ -355,10 +357,10 @@ static int sumup(const char *s, const int *offset, int top, Token **t, int *toke
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FIDENT;
t[*tokens]->u.fident=identcode("sum",3);
t[*tokens+1]->type=OPERATOR;
t[*tokens+1]->u.op=OP;
t[*tokens]->type = FIDENT;
t[*tokens]->u.fident = FUNC_SUM;
t[*tokens+1]->type = OPERATOR;
t[*tokens+1]->u.op = OP;
#if WK1DEBUG
fprintf(se,"[sum(]");
#endif
@ -411,8 +413,8 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FIDENT;
t[*tokens]->u.fident=identcode("@",1);
t[*tokens]->type = FIDENT;
t[*tokens]->u.fident = FUNC_AT_SYMBOL;
}
if (tokens) ++(*tokens);
if (pair(s+offset[top]+1,t,tokens)==-1) low=-1; else low=top;
@ -424,8 +426,8 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FIDENT;
t[*tokens]->u.fident=identcode("&",1);
t[*tokens]->type = FIDENT;
t[*tokens]->u.fident = FUNC_AMPERSAND;
#if WK1DEBUG
fprintf(se,"[&]");
#endif
@ -435,10 +437,10 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.fident=COMMA;
t[*tokens+1]->type=FIDENT;
t[*tokens+1]->u.op=identcode("&",1);
t[*tokens]->type = OPERATOR;
t[*tokens]->u.fident = COMMA;
t[*tokens+1]->type = FIDENT;
t[*tokens+1]->u.op = FUNC_AMPERSAND;
#if WK1DEBUG
fprintf(se,"[,&]");
#endif
@ -683,6 +685,8 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
}
/*}}}*/
static char internal_errbuf[4096];
/* loadwk1 -- load WK1 file */ /*{{{*/
const char *loadwk1(Sheet *sheet, const char *name)
{
@ -847,17 +851,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* INTEGER -- Integer number cell */ /*{{{*/
case 0xD:
{
Token **t;
Token t;
assert(bodylen==7);
assert(bodylen == 7);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*));
t[0]=malloc(sizeof(Token));
t[1]=(Token*)0;
t[0]->type=INT;
t[0]->u.integer=it(body+5);
putcont(sheet, tmp, t, BASE);
t.type = INT;
t.u.integer = it(body+5);
puttok(sheet, tmp, t, BASE_CONT);
format((unsigned char)body[0], cell);
break;
}
@ -865,17 +866,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* NUMBER -- Floating point number */ /*{{{*/
case 0xE:
{
Token **t;
Token t;
assert(bodylen==13);
assert(bodylen == 13);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*));
t[0]=malloc(sizeof(Token));
t[1]=(Token*)0;
t[0]->type=FLOAT;
t[0]->u.flt=dbl((unsigned char*)body+5);
putcont(sheet, tmp, t, BASE);
t.type = FLOAT;
t.u.flt = dbl((unsigned char*)body+5);
puttok(sheet, tmp, t, BASE_CONT);
format((unsigned char)body[0], cell);
break;
}
@ -883,17 +881,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* _("lL)abel") -- Label cell */ /*{{{*/
case 0xF:
{
Token **t;
Token t;
assert(bodylen>=6 && bodylen<=245);
assert(bodylen >= 6 && bodylen <= 245);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*));
t[0]=malloc(sizeof(Token));
t[1]=(Token*)0;
t[0]->type=STRING;
t[0]->u.string=strdup(body+6);
putcont(sheet, tmp, t, BASE);
t.type = STRING;
t.u.string = strdup(body+6);
puttok(sheet, tmp, t, BASE_CONT);
format((unsigned char)body[0], cell);
break;
}
@ -906,8 +901,12 @@ const char *loadwk1(Sheet *sheet, const char *name)
int tokens;
Token **t;
assert(bodylen>15);
if ((offset=malloc(it(body+13)*sizeof(int)))==0) { err=_("Out of memory"); goto ouch; }
assert(bodylen > 15);
if ((offset = malloc(it(body+13)*sizeof(int))) == 0)
{
err = _("Out of memory");
goto ouch;
}
#if WK1DEBUG
fprintf(se,"FORMULA: &(%d,%d)=",it(body+1),it(body+3));
#endif
@ -971,15 +970,25 @@ const char *loadwk1(Sheet *sheet, const char *name)
}
else
{
Token tok = eval_safe(t, LITERAL);
tvecfree(t);
if (tok.type == EEK)
{
strcpy(internal_errbuf, _("Parse error: "));
strcat(internal_errbuf, tok.u.err);
tfree(&tok);
err = internal_errbuf;
goto ouch;
}
#if WK1DEBUG
fprintf(se,"[<-- %d tokens]\r\n",tokens);
fprintf(se,"[<-- %d tokens]\r\n",tokens);
#endif
free(offset);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
cell->value.type=FLOAT;
cell->value.u.flt=dbl((unsigned char*)body+5);
putcont(sheet, tmp, t, BASE);
free(offset);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
cell->tok[CURR_VAL].type = FLOAT;
cell->tok[CURR_VAL].u.flt = dbl((unsigned char*)body+5);
puttok(sheet, tmp, tok, BASE_CONT);
}
break;
}
@ -1090,17 +1099,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* STRING -- Value of string formula */ /*{{{*/
case 0x33:
{
Token **t;
Token t;
assert(bodylen>=6 && bodylen<=245);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*));
t[0]=malloc(sizeof(Token));
t[1]=(Token*)0;
t[0]->type=STRING;
t[0]->u.string=strdup(body+5);
putcont(sheet, tmp, t, BASE);
t.type = STRING;
t.u.string = strdup(body+5);
puttok(sheet, tmp, t, BASE_CONT);
format((unsigned char)body[0], cell);
break;
}

View File

@ -21,20 +21,43 @@ array terminated by a special element.
#endif
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "default.h"
#include "display.h"
#include "eval.h"
#include "main.h"
#include "parser.h"
#include "sheet.h"
#include "xdr.h"
/*}}}*/
typedef struct Old_token_struc
{
Type type;
union
{
char *string;
double flt;
long integer;
Operator op;
char *lident;
int fident;
int location[3];
char *err;
} u;
} OldToken;
/* xdr_token */ /*{{{*/
static bool_t xdr_token(XDR *xdrs, Token *t)
static bool_t xdr_token(XDR *xdrs, OldToken *t)
{
int x,result;
if (xdrs->x_op==XDR_DECODE) (void)memset(t,0,sizeof(Token));
if (xdrs->x_op == XDR_DECODE) memset(t,0,sizeof(OldToken));
else return false;
x=t->type;
if (t->type==OPERATOR) x|=t->u.op<<8;
result=xdr_int(xdrs,&x);
@ -104,31 +127,35 @@ static bool_t xdr_token(XDR *xdrs, Token *t)
return 0;
}
/*}}}*/
/* xdr_tokenptr */ /*{{{*/
static bool_t xdr_tokenptr(XDR *xdrs, Token **t)
static bool_t xdr_tokenptr(XDR *xdrs, OldToken **t)
{
return xdr_pointer(xdrs,(char**)t,sizeof(Token),(xdrproc_t)xdr_token);
bool_t nonnull;
if (!xdr_bool(xdrs, &nonnull)) return false;
if (!nonnull) { *t = (OldToken *)0; return true; }
*t = malloc(sizeof(OldToken));
return xdr_token(xdrs, *t);
}
/*}}}*/
/* xdr_tokenptrvec */ /*{{{*/
static bool_t xdr_tokenptrvec(XDR *xdrs, Token ***t)
static bool_t xdr_tokenptrvec(XDR *xdrs, OldToken ***t)
{
unsigned int len;
int result;
assert(t!=(Token***)0);
if (xdrs->x_op!=XDR_DECODE)
{
Token **run;
if (*t==(Token**)0) len=0;
else for (len=1,run=*t; *run!=(Token*)0; ++len,++run);
}
result=xdr_array(xdrs,(char**)t,&len,65536,sizeof(Token*),(xdrproc_t)xdr_tokenptr);
if (len==0) *t=(Token**)0;
return result;
assert(t!=(OldToken***)0);
if (xdrs->x_op!=XDR_DECODE) assert(0);
if (!xdr_u_int(xdrs, &len)) return false;
*t = malloc(sizeof(OldToken*) * (len+1));
(*t)[len] = (OldToken*)0;
for (size_t i = 0; i < len; ++i)
if (!xdr_tokenptr(xdrs, *t + i)) return false;
return true;
}
/*}}}*/
/* xdr_mystring */ /*{{{*/
static bool_t xdr_mystring(XDR *xdrs, char **str)
{
@ -165,10 +192,49 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell)
{
int result,x;
assert(cell!=(Cell*)0);
if (!(xdr_tokenptrvec(xdrs, &(cell->contents[BASE]))
&& xdr_tokenptrvec(xdrs, &(cell->contents[ITERATIVE])) /* && xdr_token(xdrs, &(cell->value)) */ ))
return 0;
assert(cell != (Cell*)0);
assert(xdrs->x_op == XDR_DECODE);
OldToken **ot;
for (TokVariety tv = BASE_CONT; tv <= ITER_CONT; ++tv) {
if (!xdr_tokenptrvec(xdrs, &ot)) return false;
size_t veclen = 0;
while (ot[veclen]) ++veclen;
Token **nt = malloc(sizeof(Token*) * (veclen + 1));
nt[veclen] = NULLTOKEN;
for (size_t ti = 0; ti < veclen; ++ti)
{
nt[ti] = malloc(sizeof(Token));
cleartoken(nt[ti]);
nt[ti]->type = ot[ti]->type;
switch (ot[ti]->type) {
case EMPTY: break;
case STRING: nt[ti]->u.string = ot[ti]->u.string; break;
case FLOAT: nt[ti]->u.flt = ot[ti]->u.flt; break;
case INT: nt[ti]->u.integer = ot[ti]->u.integer; break;
case OPERATOR: nt[ti]->u.op = ot[ti]->u.op; break;
case LIDENT: nt[ti]->u.lident = ot[ti]->u.lident; break;
case FIDENT: nt[ti]->u.fident = ot[ti]->u.fident; break;
case LOCATION:
nt[ti]->u.location[0] = ot[ti]->u.location[0];
nt[ti]->u.location[1] = ot[ti]->u.location[1];
nt[ti]->u.location[2] = ot[ti]->u.location[2];
break;
case EEK:
nt[ti]->u.err = ot[ti]->u.err; break;
default:
assert(0);
}
free(ot[ti]);
}
free(ot);
Token tok = eval_safe(nt, LITERAL);
tvecfree(nt);
if (tok.type == EEK) {
tfree(&tok);
return false;
}
cell->tok[tv] = tok;
}
if (xdr_mystring(xdrs, &(cell->label))==0) return 0;
x=cell->adjust;
result=xdr_int(xdrs, &x);
@ -188,12 +254,14 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell)
return result;
}
/*}}}*/
/* xdr_column */ /*{{{*/
bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width)
{
return (xdr_int(xdrs, x) && xdr_int(xdrs, z) && xdr_int(xdrs, width));
}
/*}}}*/
/* xdr_magic */ /*{{{*/
#define MAGIC0 (('#'<<24)|('!'<<16)|('t'<<8)|'e')
#define MAGIC1 (('a'<<24)|('p'<<16)|('o'<<8)|'t')
@ -209,3 +277,150 @@ bool_t xdr_magic(XDR *xdrs)
return (xdr_long(xdrs,&m0) && m0==MAGIC0 && xdr_long(xdrs,&m1) && m1==MAGIC1 && xdr_long(xdrs,&m2) && m2==MAGIC2);
}
/*}}}*/
/* savexdr -- save a spread sheet in XDR */ /*{{{*/
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count)
{
return _("Saving in xdr format is no longer supported. Use portable text.");
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
int x,y,z;
/*}}}*/
*count=0;
if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_ENCODE);
if (!xdr_magic(&xdrs))
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
for (x=sheet->dimx-1; x>=0; --x) for (z=sheet->dimz-1; z>=0; --z)
{
int width;
int u;
width=columnwidth(sheet,x,z);
if (width!=DEF_COLUMNWIDTH)
{
u=0;
if (xdr_int(&xdrs,&u)==0 || xdr_column(&xdrs,&x,&z,&width)==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
}
for (y=sheet->dimy-1; y>=0; --y)
{
if (CELL_ATC(sheet,x,y,z)!=NULLCELL)
{
u=1;
if (xdr_int(&xdrs,&u)==0 || xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0 || xdr_cell(&xdrs,CELL_ATC(sheet,x,y,z))==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
++*count;
}
}
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
return (const char*)0;
}
/*}}}*/
/* loadxdr -- load a spread sheet in XDR */ /*{{{*/
const char *loadxdr(Sheet *sheet, const char *name)
{
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
Location w;
int width;
int u;
int olderror;
Cell *nc;
/*}}}*/
if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
if (!xdr_magic(&xdrs))
{
#if 0
xdr_destroy(&xdrs);
fclose(fp);
return _("This is not a teapot worksheet in XDR format");
#else
xdr_destroy(&xdrs);
rewind(fp);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
#endif
}
freesheet(sheet,0);
while (xdr_int(&xdrs,&u)) switch (u)
{
/* 0 -- column width element */ /*{{{*/
case 0:
{
if (xdr_column(&xdrs,&(w[X]),&(w[Z]),&width)==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
setwidth(sheet,w[X],w[Z],width);
break;
}
/*}}}*/
/* 1 -- cell element */ /*{{{*/
case 1:
{
if (xdr_int(&xdrs,&(w[X]))==0 ||
xdr_int(&xdrs,&(w[Y]))==0 ||
xdr_int(&xdrs,&(w[Z]))==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
nc = initcellofsheet(sheet, w);
if (xdr_cell(&xdrs, nc)==0)
{
freecellofsheet(sheet, w);
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
break;
}
/*}}}*/
/* default -- should not happen */ /*{{{*/
default:
{
xdr_destroy(&xdrs);
fclose(fp);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
return _("Invalid record, loading aborted");
}
/*}}}*/
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
redraw_sheet(sheet);
return (const char*)0;
}
/*}}}*/

View File

@ -17,4 +17,7 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell);
bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width);
bool_t xdr_magic(XDR *xdrs);
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count);
const char *loadxdr(Sheet *sheet, const char *name);
#endif

View File

@ -565,13 +565,15 @@ void redraw_sheet(Sheet *sheet)
else
{
cell = curcell(sheet);
print(buf+strlen(buf), bufsz-strlen(buf), 0, 1, getscientific(cell),
-1, getcont(cell, BASE));
if (getcont(cell, ITERATIVE) && mbslen(buf) < (size_t)(sheet->maxx+1-4))
Token bc = gettok(cell, BASE_CONT);
printtok(buf+strlen(buf), bufsz-strlen(buf), 0, 1, getscientific(cell),
-1, false, &bc);
Token ic = gettok(cell, ITER_CONT);
if (ic.type != EMPTY && mbslen(buf) < (size_t)(sheet->maxx+1-4))
{
strcat(buf," -> ");
print(buf+strlen(buf), bufsz-strlen(buf), 0, 1,
getscientific(cell), -1, getcont(cell, ITERATIVE));
printtok(buf+strlen(buf), bufsz-strlen(buf), 0, 1,
getscientific(cell), -1, false, &ic);
}
}
*mbspos(buf, sheet->maxx) = 0;

View File

@ -167,10 +167,11 @@ class TeapotTable {open : {public Fl_Table}}
fl_measure(s, ww, hh, 0);
if (ww > W-6) for (int i = 0; s[i]; i++) s[i] = '\#';
int adj = getadjust(cell);
Adjust adj = getadjust(cell);
fl_draw(s, xx+3, yy+3, W-6, H-6,
adj == RIGHT ? FL_ALIGN_RIGHT :
adj == LEFT ? FL_ALIGN_LEFT : FL_ALIGN_CENTER);
adj == LEFT ? FL_ALIGN_LEFT : FL_ALIGN_CENTER,
NULL, 0);
if (underlined(cell)) fl_xyline(xx, yy+H-7, xx+W);
fl_pop_clip();
@ -660,14 +661,15 @@ class MainWindow {open}
free(err);
val[sizeof(val)-1] = 0;
} else {
print(val, sizeof(val), 0, 1, getscientific(curcell(sheet)),
-1, getcont(curcell(sheet), BASE));
Token **iter = getcont(curcell(sheet), ITERATIVE);
if (iter != EMPTY_TVEC) {
Token t = gettok(curcell(sheet), BASE_CONT);
printtok(val, sizeof(val), 0, 1, getscientific(curcell(sheet)),
-1, true, &t);
t = gettok(curcell(sheet), ITER_CONT);
if (t.type != EMPTY) {
snprintf(val+strlen(val),sizeof(val)-strlen(val),
" -> ");
print(val+strlen(val), sizeof(val)-strlen(val), 0, 1,
getscientific(curcell(sheet)), -1, iter);
printtok(val+strlen(val), sizeof(val)-strlen(val), 0, 1,
getscientific(curcell(sheet)), -1, true, &t);
}
}