teapot-spreadsheet/wk1.c
Glen Whitney 08b42bf424 Prevent phantom values when clocking, resetting, and clocking again
In the end it turned out that the cause of the phantom values was
  short-cutting in getvalue() when the contents of a cell were empty,
  preventing the update of the internal cache of the value of the cell.

  However, tracking this down (and getting the associated memory management
  correct) necessitated implementing a debugging mode in which I could
  dump the internal states of cells and print various other stuff to standard
  output. It also involved understanding the meaning of various pointers in
  the code, in the process of which I renamed some commonly used macros,
  particularly the former SHEET(s,x,y,z) which was not returning a Sheet at
  all but rather a pointer to a Cell. So this macro is now called CELL_AT. I
  also replaced several very repeatedly used patterns of checking the validity
  of locations and pointers with macros, now defined in sheet.h.

  Therefore, unfortunately the (relatively small in the end) bugfix for this
  major issue is entangled with numerous textual changes to the code made
  in tracking it down.
  Fixes #18.
  Closes #19.
2019-07-24 10:47:39 -07:00

1164 lines
31 KiB
C

/* #includes */ /*{{{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 <errno.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef OLD_REALLOC
#define realloc(s,l) myrealloc(s,l)
#endif
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#include "eval.h"
#include "main.h"
#include "misc.h"
#include "sheet.h"
#include "wk1.h"
/*}}}*/
/* #defines */ /*{{{*/
#define WK1DEBUG 0
#define LOTUS123 0x0404
#define SYMPHONY 0x0405
/* Applix uses that, too */
#define EXCEL 0x0406
/*}}}*/
static int unrpn(const char *s, const int *offset, int top, Token **t, int *tokens);
#if WK1DEBUG
static FILE *se;
#endif
/* it -- convert string to int */ /*{{{*/
static int it(const char *s)
{
return (((unsigned int)((const unsigned char)*s))|(*(s+1)<<8));
}
/*}}}*/
/* dbl -- convert string to double */ /*{{{*/
static double dbl(const unsigned char *s)
{
double x;
int sg,e,i;
x=0.0;
for (i=1; i<256; i<<=1) x=x/2.0+!!(s[0]&i);
for (i=1; i<256; i<<=1) x=x/2.0+!!(s[1]&i);
for (i=1; i<256; i<<=1) x=x/2.0+!!(s[2]&i);
for (i=1; i<256; i<<=1) x=x/2.0+!!(s[3]&i);
for (i=1; i<256; i<<=1) x=x/2.0+!!(s[4]&i);
for (i=1; i<256; i<<=1) x=x/2.0+!!(s[5]&i);
x=x/2.0+!!(s[6]&0x01);
x=x/2.0+!!(s[6]&0x02);
x=x/2.0+!!(s[6]&0x04);
x=x/2.0+!!(s[6]&0x08);
x=x/2.0+1.0;
if ((e=((s[6]>>4)+((s[7]&0x7f)<<4))-1023)==-1023)
{
x=0.0;
e=0;
}
if (s[7]&0x80) sg=-1; else sg=1;
#if WK1DEBUG
fprintf(se,"%02x %02x %02x %02x %02x %02x %02x %02x ",s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7]);
fprintf(se,"%f (exp 2^%d)\r\n",sg*ldexp(x,e),e);
#endif
return (sg*ldexp(x,e));
}
/*}}}*/
/* format -- convert string into format */ /*{{{*/
static void format(unsigned char s, Cell *cell)
{
#if WK1DEBUG
fprintf(se,", format 0x%02x",s);
if (s&0x80) fprintf(se,", locked");
#endif
switch (((unsigned int)(s&0x70))>>4)
{
case 0: /* fixed with given precision */ /*{{{*/
{
cell->precision=s&0x0f;
cell->scientific=0;
break;
}
/*}}}*/
case 1: /* scientifix with given presision */ /*{{{*/
{
cell->precision=s&0x0f;
cell->scientific=1;
break;
}
/*}}}*/
case 2: /* currency with given precision */ /*{{{*/
{
cell->precision=s&0x0f;
break;
}
/*}}}*/
case 3: /* percent with given precision */ /*{{{*/
{
cell->precision=s&0x0f;
break;
}
/*}}}*/
case 4: /* comma with given precision */ /*{{{*/
{
cell->precision=s&0x0f;
break;
}
/*}}}*/
case 5: /* unused */ break;
case 6: /* unused */ break;
case 7:
{
switch (s&0x0f)
{
case 0: /* +/- */; break;
case 1: /* general */; break;
case 2: /* day-month-year */; break;
case 3: /* day-month */; break;
case 4: /* month-year */; break;
case 5: /* text */; break;
case 6: /* hidden */; break;
case 7: /* date;hour-min-sec */; break;
case 8: /* date;hour-min */; break;
case 9: /* date;intnt'l1 */; break;
case 10: /* date;intnt'l2 */; break;
case 11: /* time;intnt'l1 */; break;
case 12: /* time;intnt'l2 */; break;
case 13: /* unused13 */; break;
case 14: /* unused14 */; break;
case 15: /* default special format */; break;
}
break;
}
}
}
/*}}}*/
/* pair -- convert coordinate pair */ /*{{{*/
static int pair(const char *s, Token **t, int *tokens)
{
int x,y;
x=it(s); y=it(s+2);
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=OP;
#if WK1DEBUG
fprintf(se,"[(");
#endif
}
if (tokens) ++(*tokens);
switch (x&0xc000)
{
case 0x0000: /* MSB -> 0 0 */ /*{{{*/
{
x=x&0x2000 ? x|0xc000 : x&0x3fff;
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=INT;
t[*tokens]->u.integer=x;
#if WK1DEBUG
fprintf(se,"%d",x);
#endif
}
if (tokens) ++(*tokens);
break;
}
/*}}}*/
case 0x4000: assert(0); break;
case 0x8000: /* MSB -> 1 0 */ /*{{{*/
{
x=x&0x2000 ? x|0xc000 : x&0x3fff;
if (x!=0)
{
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;
#if WK1DEBUG
fprintf(se,"x()");
#endif
}
if (tokens) *tokens+=3;
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+1]->type=INT;
if (x<0)
{
t[*tokens]->u.op=MINUS;
t[*tokens+1]->u.integer=-x;
#if WK1DEBUG
fprintf(se,"-%d",-x);
#endif
}
else
{
t[*tokens]->u.op=PLUS;
t[*tokens+1]->u.integer=x;
#if WK1DEBUG
fprintf(se,"+%d",x);
#endif
}
}
if (tokens) *tokens+=2;
}
break;
}
/*}}}*/
case 0xc000: assert(0); break;
}
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=COMMA;
#if WK1DEBUG
fprintf(se,",");
#endif
}
if (tokens) ++(*tokens);
switch (y&0xc000)
{
case 0x0000: /* MSB -> 0 0 */ /*{{{*/
{
y=y&0x2000 ? y|0xc000 : y&0x3fff;
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=INT;
t[*tokens]->u.integer=y;
#if WK1DEBUG
fprintf(se,"%d",y);
#endif
}
if (tokens) ++(*tokens);
break;
}
/*}}}*/
case 0x4000: assert(0); break;
case 0x8000: /* MSB -> 1 0 */ /*{{{*/
{
y=y&0x2000 ? y|0xc000 : y&0x3fff;
if (y)
{
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;
#if WK1DEBUG
fprintf(se,"y()");
#endif
}
if (tokens) *tokens+=3;
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+1]->type=INT;
if (y<0)
{
if (t)
{
t[*tokens]->u.op=MINUS;
t[*tokens+1]->u.integer=-y;
#if WK1DEBUG
fprintf(se,"-%d",-y);
#endif
}
}
else
{
if (t)
{
t[*tokens]->u.op=PLUS;
t[*tokens+1]->u.integer=y;
#if WK1DEBUG
fprintf(se,"+%d",y);
#endif
}
}
}
if (tokens) *tokens+=2;
}
break;
}
/*}}}*/
case 0xc000: assert(0); break;
}
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=CP;
#if WK1DEBUG
fprintf(se,")]");
#endif
}
if (tokens) ++(*tokens);
return 0;
}
/*}}}*/
/* sumup -- sum up arguments */ /*{{{*/
static int sumup(const char *s, const int *offset, int top, Token **t, int *tokens, int argc)
{
int low;
if (top<0) return -1;
if (argc>1)
{
low=unrpn(s,offset,top,(Token**)0,(int*)0);
if (low<0) return -1;
sumup(s,offset,low-1,t,tokens,argc-1);
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=PLUS;
#if WK1DEBUG
fprintf(se,"[+]");
#endif
}
if (tokens) ++(*tokens);
}
if (s[offset[top]]==2)
{
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;
#if WK1DEBUG
fprintf(se,"[sum(]");
#endif
}
if (tokens) *tokens+=2;
}
low=unrpn(s,offset,top,t,tokens);
if (s[offset[top]]==2)
{
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=CP;
#if WK1DEBUG
fprintf(se,"[)]");
#endif
}
if (tokens) ++(*tokens);
}
return low;
}
/*}}}*/
/* unrpn -- convert RPN expression to infix */ /*{{{*/
static int unrpn(const char *s, const int *offset, int top, Token **t, int *tokens)
{
int low;
if (top<0) return -1;
switch (s[offset[top]])
{
case 0: /* double constant */ /*{{{*/
{
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FLOAT;
t[*tokens]->u.flt=dbl((const unsigned char*)s+offset[top]+1);
#if WK1DEBUG
fprintf(se,"[constant %f]",dbl((const unsigned char*)s+offset[top]+1));
#endif
}
if (tokens) ++(*tokens);
low=top;
break;
}
/*}}}*/
case 1: /* variable */ /*{{{*/
{
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FIDENT;
t[*tokens]->u.fident=identcode("@",1);
}
if (tokens) ++(*tokens);
if (pair(s+offset[top]+1,t,tokens)==-1) low=-1; else low=top;
break;
}
/*}}}*/
case 2: /* range */ /*{{{*/
{
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=FIDENT;
t[*tokens]->u.fident=identcode("&",1);
#if WK1DEBUG
fprintf(se,"[&]");
#endif
}
if (tokens) ++(*tokens);
pair(s+offset[top]+1,t,tokens);
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);
#if WK1DEBUG
fprintf(se,"[,&]");
#endif
}
if (tokens) *tokens+=2;
pair(s+offset[top]+5,t,tokens);
low=top;
break;
}
/*}}}*/
case 3: /* return */ /*{{{*/
{
low=unrpn(s,offset,top-1,t,tokens);
if (t)
{
t[*tokens]=(Token*)0;
#if WK1DEBUG
fprintf(se,"[RETURN]");
#endif
}
if (tokens) ++(*tokens);
break;
}
/*}}}*/
case 4: /* paren */ /*{{{*/
{
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=OP;
#if WK1DEBUG
fprintf(se,"[(]");
#endif
}
if (tokens) ++(*tokens);
low=unrpn(s,offset,top-1,t,tokens);
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=CP;
#if WK1DEBUG
fprintf(se,"[)]");
#endif
}
if (tokens) ++(*tokens);
break;
}
/*}}}*/
case 5: /* int constant */ /*{{{*/
{
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=INT;
t[*tokens]->u.integer=it(s+offset[top]+1);
#if WK1DEBUG
fprintf(se,"[constant %d]",it(s+offset[top]+1));
#endif
}
if (tokens) ++(*tokens);
low=top;
break;
}
/*}}}*/
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19: /* +, -, *, /, ^, -, !=, <=, >=, <, > */ /*{{{*/
{
if (t)
{
low=unrpn(s,offset,top-1,(Token**)0,(int*)0);
low=unrpn(s,offset,low-1,t,tokens);
if (low<0) return -1;
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
switch (s[offset[top]])
{
case 9: /* + */ /*{{{*/
{
t[*tokens]->u.op=PLUS;
#if WK1DEBUG
fprintf(se,"[+]");
#endif
break;
}
/*}}}*/
case 10: /* - */ /*{{{*/
{
t[*tokens]->u.op=MINUS;
#if WK1DEBUG
fprintf(se,"[-]");
#endif
break;
}
/*}}}*/
case 11: /* * */ /*{{{*/
{
t[*tokens]->u.op=MUL;
#if WK1DEBUG
fprintf(se,"[*]");
#endif
break;
}
/*}}}*/
case 12: /* / */ /*{{{*/
{
t[*tokens]->u.op=DIV;
#if WK1DEBUG
fprintf(se,"[/]");
#endif
break;
}
/*}}}*/
case 13: /* ^ */ /*{{{*/
{
t[*tokens]->u.op=POW;
#if WK1DEBUG
fprintf(se,"[^]");
#endif
break;
}
/*}}}*/
case 14: /* == */ /*{{{*/
{
t[*tokens]->u.op=ISEQUAL;
#if WK1DEBUG
fprintf(se,"[==]");
#endif
break;
}
/*}}}*/
case 15: /* != */ /*{{{*/
{
t[*tokens]->u.op=NE;
#if WK1DEBUG
fprintf(se,"[!=]");
#endif
break;
}
/*}}}*/
case 16: /* <= */ /*{{{*/
{
t[*tokens]->u.op=LE;
#if WK1DEBUG
fprintf(se,"[<=]");
#endif
break;
}
/*}}}*/
case 17: /* >= */ /*{{{*/
{
t[*tokens]->u.op=GE;
#if WK1DEBUG
fprintf(se,"[>=]");
#endif
break;
}
/*}}}*/
case 18: /* < */ /*{{{*/
{
t[*tokens]->u.op=LT;
#if WK1DEBUG
fprintf(se,"[<]");
#endif
break;
}
/*}}}*/
case 19: /* > */ /*{{{*/
{
t[*tokens]->u.op=GT;
#if WK1DEBUG
fprintf(se,"[>]");
#endif
break;
}
/*}}}*/
default: assert(0);
}
if (tokens) ++(*tokens);
unrpn(s,offset,top-1,t,tokens);
}
else
{
low=unrpn(s,offset,top-1,(Token**)0,tokens);
if (tokens) ++(*tokens);
low=unrpn(s,offset,low-1,(Token**)0,tokens);
}
break;
}
/*}}}*/
case 23: /* unary + */ /*{{{*/
{
low=unrpn(s,offset,top-1,t,tokens);
break;
}
/*}}}*/
case 80: /* sum */ /*{{{*/
{
int argc;
argc=s[offset[top]+1];
#if WK1DEBUG
if (t) fprintf(se,"[sum argc=%d]",argc);
#endif
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=OP;
#if WK1DEBUG
fprintf(se,"[(]");
#endif
}
if (tokens) ++(*tokens);
low=sumup(s,offset,top-1,t,tokens,argc);
if (t)
{
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type=OPERATOR;
t[*tokens]->u.op=CP;
#if WK1DEBUG
fprintf(se,"[)]");
#endif
}
if (tokens) ++(*tokens);
break;
}
/*}}}*/
default: assert(0); low=-1;
}
return (low<0 ? -1 : low);
}
/*}}}*/
/* loadwk1 -- load WK1 file */ /*{{{*/
const char *loadwk1(Sheet *sheet, const char *name)
{
/* variables */ /*{{{*/
FILE *fp;
const char *err;
int head[4];
char *body=(char*)0,*newbody;
size_t bodymaxlen=0;
size_t bodylen;
int found_bof=0,found_eof=0;
/*}}}*/
#if WK1DEBUG
se=fopen("/dev/tty","w"); assert(se!=(FILE*)0); fprintf(se,"\r\n");
#endif
if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
err=(const char*)0;
while (1)
{
/* read header */ /*{{{*/
if ((head[0]=getc(fp))==EOF) break;
if ((head[1]=getc(fp))==EOF) { err=_("The record header appears to be truncated"); goto ouch; }
if ((head[2]=getc(fp))==EOF) { err=_("The record header appears to be truncated"); goto ouch; }
if ((head[3]=getc(fp))==EOF) { err=_("The record header appears to be truncated"); goto ouch; }
bodylen=head[2]|(head[3]<<8);
/*}}}*/
/* read body */ /*{{{*/
if (bodylen>bodymaxlen)
{
newbody=realloc(body,bodymaxlen=bodylen);
if (newbody==(char*)0) { err=_("Out of memory"); goto ouch; }
else body=newbody;
}
if (bodylen) if (fread(body,bodylen,1,fp)!=1) { err=_("The record body appears to be truncated"); goto ouch; }
/*}}}*/
/* process record */ /*{{{*/
#if WK1DEBUG
fprintf(se,"bodylen %d, type %04x\r\n",bodylen,head[0]|(head[1]<<8));
#endif
switch (head[0]|(head[1]<<8))
{
/* BOF -- Beginning of file */ /*{{{*/
case 0x0:
{
if (bodylen!=2) { err=_("Invalid record body length"); goto ouch; }
if (!found_bof)
{
freesheet(sheet,0);
found_bof=it(body);
}
break;
}
/*}}}*/
/* EOF -- End of file */ /*{{{*/
case 0x1:
{
if (bodylen!=0) { err=_("Invalid record body length"); goto ouch; }
found_eof=1;
break;
}
/*}}}*/
/* CALCMODE -- Calculation mode */ /*{{{*/
case 0x2:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* (unsigned char)body[0] means: */
/* 0 -- manual */
/* 0xff -- automatic */
break;
}
/*}}}*/
/* CALCORDER -- Calculation order */ /*{{{*/
case 0x3:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* (unsigned char)body[0] means: */
/* 0 -- natural */
/* 1 -- by column */
/* 0xff -- by row */
break;
}
/*}}}*/
/* SPLIT -- Split window type */ /*{{{*/
case 0x4:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* (unsigned)body[0] means: */
/* 0: not split */
/* 1: vertical split */
/* 0xff: horizontal split */
break;
}
/*}}}*/
/* SYNC -- Split window sync */ /*{{{*/
case 0x5:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* (unsigned)body[0] means: */
/* 0: not synchronized */
/* 0xff: synchronized */
break;
}
/*}}}*/
/* RANGE -- Active worksheet range */ /*{{{*/
case 0x6:
{
if (bodylen!=8) { err=_("Invalid record body length"); goto ouch; }
resize(sheet,it(body+4),it(body+6),0);
/* range is from &(it(body),it(body+2)) to &(it(body+4),it(body+6)) */
break;
}
/*}}}*/
/* WINDOW1 -- Window 1 record */ /*{{{*/
case 0x7:
{
/* 31 is the specification, but Applix generates 32 while claiming to be Excel */
if (bodylen!=31 && (found_bof!=EXCEL && bodylen!=32)) { err=_("Invalid record body length"); goto ouch; }
break;
}
/*}}}*/
/* COLW1 -- Column width, window 1 */ /*{{{*/
case 0x8:
{
if (bodylen!=3) { err=_("Invalid record body length"); goto ouch; }
break;
}
/*}}}*/
/* WINTWO -- Window 2 record */ /*{{{*/
case 0x9:
{
if (bodylen!=31) { err=_("Invalid record body length"); goto ouch; }
break;
}
/*}}}*/
/* COLW2 -- Column width, window 2 */ /*{{{*/
case 0xA:
{
if (bodylen!=3) { err=_("Invalid record body length"); goto ouch; }
break;
}
/*}}}*/
/* _("nN)ame") -- Named range */ /*{{{*/
case 0xB:
{
if (bodylen!=24) { err=_("Invalid record body length"); goto ouch; }
break;
}
/*}}}*/
/* BLANK -- Blank cell */ /*{{{*/
case 0xC:
{
if (bodylen!=5) { err=_("Invalid record body length"); goto ouch; }
initcell(sheet,it(body+1),it(body+3),0);
format((unsigned char)body[0],CELL_AT(sheet,it(body+1),it(body+3),0));
break;
}
/*}}}*/
/* INTEGER -- Integer number cell */ /*{{{*/
case 0xD:
{
Token **t;
assert(bodylen==7);
initcell(sheet,it(body+1),it(body+3),0);
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,it(body+1),it(body+3),0,t,0);
format((unsigned char)body[0],CELL_AT(sheet,it(body+1),it(body+3),0));
break;
}
/*}}}*/
/* NUMBER -- Floating point number */ /*{{{*/
case 0xE:
{
Token **t;
assert(bodylen==13);
initcell(sheet,it(body+1),it(body+3),0);
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,it(body+1),it(body+3),0,t,0);
format((unsigned char)body[0],CELL_AT(sheet,it(body+1),it(body+3),0));
break;
}
/*}}}*/
/* _("lL)abel") -- Label cell */ /*{{{*/
case 0xF:
{
Token **t;
assert(bodylen>=6 && bodylen<=245);
initcell(sheet,it(body+1),it(body+3),0);
t=malloc(2*sizeof(Token*));
t[0]=malloc(sizeof(Token));
t[1]=(Token*)0;
t[0]->type=STRING;
t[0]->u.string=mystrmalloc(body+6);
putcont(sheet,it(body+1),it(body+3),0,t,0);
format((unsigned char)body[0],CELL_AT(sheet,it(body+1),it(body+3),0));
break;
}
/*}}}*/
/* FORMULA -- Formula cell */ /*{{{*/
case 0x10:
{
int i,j,size;
int *offset;
int tokens;
Token **t;
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
for (i=15,size=it(body+13)+15,j=0; i<size; ++i,++j)
{
offset[j]=i;
switch(body[i])
{
case 0: /* double constant */ /*{{{*/
{
i+=8;
break;
}
/*}}}*/
case 1: /* variable */ /*{{{*/
{
i+=4;
break;
}
/*}}}*/
case 2: /* range */ /*{{{*/
{
i+=8;
break;
}
/*}}}*/
case 5: /* int constant */ /*{{{*/
{
i+=2;
break;
}
/*}}}*/
case 80: /* sum */ /*{{{*/
{
++i;
break;
}
/*}}}*/
}
}
#if WK1DEBUG
fprintf(se,", value %f",dbl((unsigned char*)body+5));
#endif
format((unsigned char)body[0],CELL_AT(sheet,it(body+1),it(body+3),0));
#if WK1DEBUG
fprintf(se,"\r\n");
#endif
tokens=0; unrpn(body,offset,j-1,(Token**)0,&tokens);
if ((t=malloc(tokens*sizeof(Token*)))==(Token**)0)
{
free(offset);
err=_("Out of memory");
goto ouch;
}
for (; tokens; --tokens) t[tokens-1]=(Token*)0;
if (unrpn(body,offset,j-1,t,&tokens)==-1)
{
err=_("Out of memory");
tvecfree(t);
goto ouch;
}
else
{
#if WK1DEBUG
fprintf(se,"[<-- %d tokens]\r\n",tokens);
#endif
free(offset);
initcell(sheet,it(body+1),it(body+3),0);
CELL_AT(sheet,it(body+1),it(body+3),0)->value.type=FLOAT;
CELL_AT(sheet,it(body+1),it(body+3),0)->value.u.flt=dbl((unsigned char*)body+5);
putcont(sheet,it(body+1),it(body+3),0,t,0);
}
break;
}
/*}}}*/
/* TABLE -- Data table range */ /*{{{*/
case 0x18: assert(bodylen==25); break;
/*}}}*/
/* ORANGE/QRANGE -- Query range */ /*{{{*/
case 0x19: assert(bodylen==25); break;
/*}}}*/
/* PRANGE -- Print range */ /*{{{*/
case 0x1A: assert(bodylen==8); break;
/*}}}*/
/* SRANGE -- Sort range */ /*{{{*/
case 0x1B: assert(bodylen==8); break;
/*}}}*/
/* FRANGE -- Fill range */ /*{{{*/
case 0x1C: assert(bodylen==8); break;
/*}}}*/
/* KRANGE1 -- Primary sort key range */ /*{{{*/
case 0x1D: assert(bodylen==9); break;
/*}}}*/
/* HRANGE -- Distribution range */ /*{{{*/
case 0x20: assert(bodylen==16); break;
/*}}}*/
/* KRANGE2 -- Secondary sort key range */ /*{{{*/
case 0x23: assert(bodylen==9); break;
/*}}}*/
/* PROTEC -- Global protection */ /*{{{*/
case 0x24:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* (unsigned)body[0] means: */
/* 0: off */
/* 0xff: on */
break;
}
/*}}}*/
/* FOOTER -- Print footer */ /*{{{*/
case 0x25:
{
if (body[bodylen-1]!='\0' || bodylen<1 || bodylen>243) { err=_("Invalid record body length"); goto ouch; }
break;
}
/*}}}*/
/* HEADER -- Print header */ /*{{{*/
case 0x26:
{
if (body[bodylen-1]!='\0' || bodylen<1 || bodylen>243) { err=_("Invalid record body length"); goto ouch; }
break;
}
/*}}}*/
/* SETUP -- Print setup */ /*{{{*/
case 0x27: assert(bodylen==40); break;
/*}}}*/
/* MARGINS -- Print margins code */ /*{{{*/
case 0x28: assert(bodylen==10); break;
/*}}}*/
/* LABELFMT -- Label alignment */ /*{{{*/
case 0x29:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* (unsigned char)body[0] means: */
/* 0x22: right aligned labels */
/* 0x27: left aligned labels */
/* 0x5e: centered labels */
break;
}
/*}}}*/
/* TITLES -- Print borders */ /*{{{*/
case 0x2A: assert(bodylen==16); break;
/*}}}*/
/* GRAPH -- Current graph settings */ /*{{{*/
case 0x2D:
{
/* The specification says bodylen is 437, Excel 5 says it are */
/* 443 bytes. We better silently ignore this. */
break;
}
/*}}}*/
/* NGRAPH -- Named graph settings */ /*{{{*/
case 0x2E: assert(bodylen==453); break;
/*}}}*/
/* CALCCOUNT -- Iteration count */ /*{{{*/
case 0x2F:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* Do up to %d Iterations */
break;
}
/*}}}*/
/* UNFORMATTED -- Formatted/unformatted print */ /*{{{*/
case 0x30: assert(bodylen==1); break;
/*}}}*/
/* CURSORW12 -- Cursor location */ /*{{{*/
case 0x31:
{
if (bodylen!=1) { err=_("Invalid record body length"); goto ouch; }
/* (unsigned)body[0] means cursor in window: */
/* 1: 1 */
/* 2: 2 */
break;
}
/*}}}*/
/* WINDOW -- Symphony window settings */ /*{{{*/
case 0x32: assert(bodylen==144); break;
/*}}}*/
/* STRING -- Value of string formula */ /*{{{*/
case 0x33:
{
Token **t;
assert(bodylen>=6 && bodylen<=245);
initcell(sheet,it(body+1),it(body+3),0);
t=malloc(2*sizeof(Token*));
t[0]=malloc(sizeof(Token));
t[1]=(Token*)0;
t[0]->type=STRING;
t[0]->u.string=mystrmalloc(body+5);
putcont(sheet,it(body+1),it(body+3),0,t,0);
format((unsigned char)body[0],CELL_AT(sheet,it(body+1),it(body+3),0));
break;
}
/*}}}*/
/* PASSWORD -- File lockout (CHKSUM) */ /*{{{*/
case 0x37: assert(bodylen==4); break;
/*}}}*/
/* LOCKED -- Lock flag */ /*{{{*/
case 0x38: assert(bodylen==1); break;
/*}}}*/
/* QUERY -- Symphony query settings */ /*{{{*/
case 0x3C: assert(bodylen==127); break;
/*}}}*/
/* QUERYNAME -- Query name */ /*{{{*/
case 0x3D: assert(bodylen==16); break;
/*}}}*/
/* PRINT -- Symphony print record */ /*{{{*/
case 0x3E: assert(bodylen==679); break;
/*}}}*/
/* PRINTNAME -- Print record name */ /*{{{*/
case 0x3F: assert(bodylen==16); break;
/*}}}*/
/* GRAPH2 -- Symphony graph record */ /*{{{*/
case 0x40: assert(bodylen==499); break;
/*}}}*/
/* GRAPHNAME -- Graph record name */ /*{{{*/
case 0x41: assert(bodylen==16); break;
/*}}}*/
/* ZOOM -- Orig coordinates expanded window */ /*{{{*/
case 0x42: assert(bodylen==9); break;
/*}}}*/
/* SYMSPLIT -- No. of split windows */ /*{{{*/
case 0x43: assert(bodylen==2); break;
/*}}}*/
/* NSROWS -- No. of screen rows */ /*{{{*/
case 0x44: assert(bodylen==2); break;
/*}}}*/
/* NSCOLS -- No. of screen columns */ /*{{{*/
case 0x45: assert(bodylen==2); break;
/*}}}*/
/* RULER -- Named ruler range */ /*{{{*/
case 0x46: assert(bodylen==25); break;
/*}}}*/
/* NNAME -- Named sheet range */ /*{{{*/
case 0x47: assert(bodylen==25); break;
/*}}}*/
/* ACOMM -- Autoload.comm code */ /*{{{*/
case 0x48: assert(bodylen==65); break;
/*}}}*/
/* AMACRO -- Autoexecute macro address */ /*{{{*/
case 0x49: assert(bodylen==8); break;
/*}}}*/
/* PARSE -- Query parse information */ /*{{{*/
case 0x4A: assert(bodylen==16); break;
/*}}}*/
}
/*}}}*/
if (!found_bof) { err=_("This is not a WK1 file"); goto ouch; }
}
if (!found_eof) err=_("File truncated");
ouch:
if (body) free(body);
if (fclose(fp)==EOF && err==(const char*)0) err=strerror(errno);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
return err;
}
/*}}}*/