teapot-spreadsheet/src/common/xdr.c

435 lines
10 KiB
C

/* Notes */ /*{{{C}}}*//*{{{*/
/*
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;
long 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;
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_long(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)
{
unsigned int len;
int 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)
{
static struct xdr_discrim arms[3]=
{
{ 0, (xdrproc_t)xdr_void },
{ 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;
result = xdr_int(xdrs, &x);
cell->adjust = x;
if (result==0) return 0;
if (xdr_int(xdrs, &(cell->precision))==0) return 0;
x = 0;
/* 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 */
cell->shadowed = ((x & (1<<1)) != 0);
if ((x & (1<<2)) !=0 ) cell->fform = FLT_SCIENTIFIC;
cell->locked = ((x & (1<<3)) != 0);
cell->transparent = ((x & (1<<4)) != 0);
cell->ignored = ((x & (1<<5)) != 0);
cell->bold = ((x & (1<<6)) != 0);
cell->underline = ((x & (1<<7)) != 0);
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')
#define MAGIC2 (('\n'<<24)|('x'<<16)|('d'<<8)|'r')
bool_t xdr_magic(XDR *xdrs)
{
long m0,m1,m2;
m0=MAGIC0;
m1=MAGIC1;
m2=MAGIC2;
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, 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);
redraw_sheet(sheet);
return (const char*)0;
}
/*}}}*/