470 lines
11 KiB
C
470 lines
11 KiB
C
/* Notes */ /*{{{C}}}*//*{{{*/
|
|
/*
|
|
This file contains code to read the legacy binary XDR format for
|
|
spreadsheet files, typically written in files with the .tp extension.
|
|
|
|
xdr_enum() is unusable, because enum_t may have a different size than
|
|
an enum. The construction
|
|
|
|
int_value=*enum_value;
|
|
result=xdr_int(xdrs,&int_value);
|
|
*enum_value=int_value;
|
|
return result;
|
|
|
|
solves the problem and works for both encoding and decoding.
|
|
Unfortunately, I could not yet find such a solution for a variable sized
|
|
array terminated by a special element.
|
|
|
|
*/
|
|
/*}}}*/
|
|
/* #includes */ /*{{{*/
|
|
#ifdef DMALLOC
|
|
#include "dmalloc.h"
|
|
#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;
|
|
int32_t integer;
|
|
Operator op;
|
|
char *lident;
|
|
int fident;
|
|
int location[3];
|
|
char *err;
|
|
} u;
|
|
} OldToken;
|
|
|
|
/* xdr_token */ /*{{{*/
|
|
static bool_t xdr_token(XDR *xdrs, OldToken *t)
|
|
{
|
|
int x,result;
|
|
|
|
if (xdrs->x_op == XDR_DECODE) memset(t,0,sizeof(OldToken));
|
|
else return false;
|
|
/* Since we are only decoding, we do not have to do anything to x
|
|
in advance; these lines are just kept for reference
|
|
x=t->type;
|
|
if (t->type==OPERATOR) x|=t->u.op<<8;
|
|
*/
|
|
result=xdr_int(xdrs,&x);
|
|
if ((x&0xff)==OPERATOR) t->u.op=(x>>8)&0xff;
|
|
t->type=x&0xff;
|
|
if (result==0) return result;
|
|
switch (t->type)
|
|
{
|
|
/* EMPTY */ /*{{{*/
|
|
case EMPTY:
|
|
{
|
|
return 1;
|
|
}
|
|
/*}}}*/
|
|
/* STRING */ /*{{{*/
|
|
case STRING:
|
|
{
|
|
return xdr_wrapstring(xdrs,&(t->u.string));
|
|
}
|
|
/*}}}*/
|
|
/* FLOAT */ /*{{{*/
|
|
case FLOAT:
|
|
{
|
|
return xdr_double(xdrs,&(t->u.flt));
|
|
}
|
|
/*}}}*/
|
|
/* INT */ /*{{{*/
|
|
case INT:
|
|
{
|
|
return xdr_int32_t(xdrs,&(t->u.integer));
|
|
}
|
|
/*}}}*/
|
|
/* OPERATOR */ /*{{{*/
|
|
case OPERATOR:
|
|
{
|
|
return 1; /* since op is encoded in type */
|
|
}
|
|
/*}}}*/
|
|
/* LIDENT */ /*{{{*/
|
|
case LIDENT:
|
|
{
|
|
return xdr_wrapstring(xdrs,&(t->u.lident));
|
|
}
|
|
/*}}}*/
|
|
/* FIDENT */ /*{{{*/
|
|
case FIDENT:
|
|
{
|
|
return xdr_int(xdrs,&(t->u.fident));
|
|
}
|
|
/*}}}*/
|
|
/* LOCATION */ /*{{{*/
|
|
case LOCATION:
|
|
{
|
|
return (xdr_int(xdrs,&(t->u.location[0])) && xdr_int(xdrs,&(t->u.location[1])) && xdr_int(xdrs,&(t->u.location[2])));
|
|
}
|
|
/*}}}*/
|
|
/* EEK */ /*{{{*/
|
|
case EEK:
|
|
{
|
|
return xdr_wrapstring(xdrs,&(t->u.err));
|
|
}
|
|
/*}}}*/
|
|
/* default -- should not happen */ /*{{{*/
|
|
default: assert(0);
|
|
/*}}}*/
|
|
}
|
|
return 0;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* xdr_tokenptr */ /*{{{*/
|
|
static bool_t xdr_tokenptr(XDR *xdrs, OldToken **t)
|
|
{
|
|
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, OldToken ***t)
|
|
{
|
|
assert(t != (OldToken***)0);
|
|
if (xdrs->x_op != XDR_DECODE) assert(0);
|
|
unsigned int len;
|
|
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;
|
|
}
|
|
/*}}}*/
|
|
|
|
static bool_t xdr_myvoid(XDR *xdrs, void* data) {
|
|
return 1;
|
|
}
|
|
|
|
/* xdr_mystring */ /*{{{*/
|
|
static bool_t xdr_mystring(XDR *xdrs, char **str)
|
|
{
|
|
static struct xdr_discrim arms[3]=
|
|
{
|
|
{ 0, (xdrproc_t)xdr_myvoid },
|
|
{ 1, (xdrproc_t)xdr_wrapstring },
|
|
{ -1, (xdrproc_t)0 }
|
|
};
|
|
enum_t x;
|
|
int res;
|
|
|
|
x=(*str!=(char*)0);
|
|
res=xdr_union(xdrs, &x, (char*)str, arms, (xdrproc_t)0);
|
|
if (!x) *str=(char*)0;
|
|
return res;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* Notes */ /*{{{*/
|
|
/*
|
|
|
|
The saved sheet consists of three xdr_int()s which specify x, y and z
|
|
position of the cell saved with xdr_cell(). Perhaps xdr_cell could be
|
|
given those as parameters, which would be more correct concerning the
|
|
purpose of the xdr_functions. Then again, reading the position may
|
|
fail (eof), whereas after the position has been read, xdr_cell() must
|
|
not fail when loading a sheet.
|
|
|
|
*/
|
|
/*}}}*/
|
|
/* xdr_cell */ /*{{{*/
|
|
bool_t xdr_cell(XDR *xdrs, Cell *cell)
|
|
{
|
|
int result,x;
|
|
|
|
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; ONLY DECODING */
|
|
result = xdr_int(xdrs, &x);
|
|
cleartoken(cell->tok + STYLE_CONT);
|
|
Adjust a = (Adjust)x;
|
|
if (a != AUTOADJUST) {
|
|
cell->tok[STYLE_CONT].type = STYLE;
|
|
cell->tok[STYLE_CONT].u.style.adjust = a;
|
|
}
|
|
if (result==0) return 0;
|
|
if (xdr_int(xdrs, &x)==0) return 0;
|
|
if (x != NO_PRECISION) {
|
|
cell->tok[STYLE_CONT].type = STYLE;
|
|
cell->tok[STYLE_CONT].u.style.precision = (PrecisionLevel)x;
|
|
}
|
|
/* Since we only decode, we do not need to do this any more; it is
|
|
just kept for documentation purposes:
|
|
|
|
x = (cell->updated & 1) | ((cell->shadowed & 1)<<1)
|
|
| ((cell->scientific & 1)<<2) | ((cell->locked & 1)<<3)
|
|
| ((cell->transparent & 1)<<4) | ((cell->ignored & 1)<<5)
|
|
| ((cell->bold & 1)<<6) | ((cell->underline & 1)<<7);
|
|
*/
|
|
result = xdr_int(xdrs, &x);
|
|
/* cell->updated = ((x&(1))!=0); # Makes no sense to restore this */
|
|
if ((x & (1<<1)) != 0) {
|
|
cell->tok[STYLE_CONT].type = STYLE;
|
|
cell->tok[STYLE_CONT].u.style.shadowed = true;
|
|
cell->tok[STYLE_CONT].u.style.shadowed_set = true;
|
|
}
|
|
if ((x & (1<<2)) !=0 ) {
|
|
cell->tok[STYLE_CONT].type = STYLE;
|
|
cell->tok[STYLE_CONT].u.style.fform = FLT_SCIENTIFIC;
|
|
}
|
|
cell->locked = ((x & (1<<3)) != 0);
|
|
if ((x & (1<<4)) != 0) {
|
|
cell->tok[STYLE_CONT].type = STYLE;
|
|
cell->tok[STYLE_CONT].u.style.transparent = true;
|
|
cell->tok[STYLE_CONT].u.style.transparent_set = true;
|
|
}
|
|
cell->ignored = ((x & (1<<5)) != 0);
|
|
if ((x & (1<<6)) != 0) {
|
|
cell->tok[STYLE_CONT].type = STYLE;
|
|
cell->tok[STYLE_CONT].u.style.bold = true;
|
|
cell->tok[STYLE_CONT].u.style.bold_set = true;
|
|
}
|
|
if ((x & (1<<7)) != 0) {
|
|
cell->tok[STYLE_CONT].type = STYLE;
|
|
cell->tok[STYLE_CONT].u.style.underline = true;
|
|
cell->tok[STYLE_CONT].u.style.underline_set = true;
|
|
}
|
|
return result;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* xdr_column */ /*{{{*/
|
|
bool_t xdr_column(XDR *xdrs, int *x, int *z, ColWidT *width)
|
|
{
|
|
int dummy = 0;
|
|
bool_t retval = xdr_int(xdrs, x) && xdr_int(xdrs, z) && xdr_int(xdrs, &dummy);
|
|
*width = (ColWidT)dummy;
|
|
return retval;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* xdr_magic */ /*{{{*/
|
|
#define MAGIC0 (('#'<<24)|('!'<<16)|('t'<<8)|'e')
|
|
#define MAGIC1 (('a'<<24)|('p'<<16)|('o'<<8)|'t')
|
|
#define MAGIC2 (('\n'<<24)|('x'<<16)|('d'<<8)|'r')
|
|
|
|
bool_t xdr_magic(XDR *xdrs)
|
|
{
|
|
int32_t m0 = MAGIC0, m1 = MAGIC1, m2 = MAGIC2;
|
|
return ( xdr_int32_t(xdrs, &m0) && m0 == MAGIC0
|
|
&& xdr_int32_t(xdrs, &m1) && m1 == MAGIC1
|
|
&& xdr_int32_t(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.");
|
|
|
|
/* The below code no longer compiles, so saving it as a comment for reference
|
|
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;
|
|
ColWidT 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, NULL);
|
|
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);
|
|
return NULL;
|
|
}
|
|
/*}}}*/
|