/* #includes */ /*{{{C}}}*//*{{{*/ #ifdef DMALLOC #include "dmalloc.h" #endif #include #include #include #include #include #include #include "csv.h" #include "default.h" #include "display.h" #include "eval.h" #include "main.h" #include "misc.h" #include "parser.h" #include "scanner.h" #include "sheet.h" #include "utf8.h" #include "xdr.h" /*}}}*/ /* #defines */ /*{{{*/ #define SHEET(s,x,y,z) (*(s->sheet+(x)*s->dimz*s->dimy+(y)*s->dimz+(z))) #define HASH(x,s) \ { \ const unsigned char *S=(const unsigned char*)s; \ \ x=0; \ while (*S) { x=(x<<5)+((x>>27)^*S); ++S; } \ x%=LABEL_CACHE; \ } #define SHADOWED(sheet,x,y,z) (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->shadowed) /*}}}*/ /* variables */ /*{{{*/ static int upd_clock; /* evaluate clocked expressions */ /* Used during evaluation of a cell to specify the currently updated cell */ Sheet *upd_sheet; int upd_x; int upd_y; int upd_z; int max_eval; /*}}}*/ /* copycell -- copy a cell */ /*{{{*/ static void copycell(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2) { /* variables */ /*{{{*/ Token **run; int i,len; /*}}}*/ assert(sheet1!=(Sheet*)0); assert(sheet2!=(Sheet*)0); if (x1dimx && y1dimy && z1dimz) { sheet2->changed=1; if (SHEET(sheet1,x1,y1,z1)==(Cell*)0) freecell(sheet2,x2,y2,z2); else /* copy first cell to second */ /*{{{*/ { freecell(sheet2,x2,y2,z2); initcell(sheet2,x2,y2,z2); memcpy(SHEET(sheet2,x2,y2,z2),SHEET(sheet1,x1,y1,z1),sizeof(Cell)); if (SHEET(sheet1,x1,y1,z1)->contents!=(Token**)0) { for (len=1,run=SHEET(sheet1,x1,y1,z1)->contents; *run!=(Token*)0; ++len,++run); SHEET(sheet2,x2,y2,z2)->contents=malloc(len*sizeof(Token*)); for (i=0; icontents+i)==(Token*)0) *(SHEET(sheet2,x2,y2,z2)->contents+i)=(Token*)0; else { *(SHEET(sheet2,x2,y2,z2)->contents+i)=malloc(sizeof(Token)); **(SHEET(sheet2,x2,y2,z2)->contents+i)=tcopy(**(SHEET(sheet1,x1,y1,z1)->contents+i)); } } } if (SHEET(sheet1,x1,y1,z1)->ccontents!=(Token**)0) { for (len=1,run=SHEET(sheet1,x1,y1,z1)->ccontents; *run!=(Token*)0; ++len,++run); SHEET(sheet2,x2,y2,z2)->ccontents=malloc(len*sizeof(Token*)); for (i=0; iccontents+i)==(Token*)0) *(SHEET(sheet2,x2,y2,z2)->ccontents+i)=(Token*)0; else { *(SHEET(sheet2,x2,y2,z2)->ccontents+i)=malloc(sizeof(Token)); **(SHEET(sheet2,x2,y2,z2)->ccontents+i)=tcopy(**(SHEET(sheet1,x1,y1,z1)->ccontents+i)); } } } if (SHEET(sheet1,x1,y1,z1)->label!=(char*)0) SHEET(sheet2,x2,y2,z2)->label=strcpy(malloc(strlen(SHEET(sheet1,x1,y1,z1)->label)+1),SHEET(sheet1,x1,y1,z1)->label); SHEET(sheet2,x2,y2,z2)->value.type=EMPTY; } /*}}}*/ } else freecell(sheet2,x2,y2,z2); } /*}}}*/ /* swapblock -- swap two non-overlapping blocks of cells */ /*{{{*/ static void swapblock(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2, int xdist, int ydist, int zdist) { int xoff, yoff, zoff; assert(sheet1!=(Sheet*)0); assert(sheet2!=(Sheet*)0); resize(sheet1,x1+xdist-1,y1+ydist-1,z1+zdist-1); resize(sheet2,x2+xdist-1,y2+ydist-1,z2+zdist-1); for (xoff=0; xoffchanged=1; sheet2->changed=1; } /*}}}*/ /* cmpcell -- compare to cells with given order flags */ /*{{{*/ /* Notes */ /*{{{*/ /* Compare the _values_ of two cells. The result is -1 if first is smaller than second, 0 if they are equal and 1 if the first is bigger than the 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) { assert(sheet1!=(Sheet*)0); assert(sheet2!=(Sheet*)0); /* empty cells are smaller than any non-empty cell */ /*{{{*/ if (x1>=sheet1->dimx || y1>=sheet1->dimy || z1>=sheet1->dimz || SHEET(sheet1,x1,y1,z1)==(Cell*)0 || SHEET(sheet1,x1,y1,z1)->value.type==EMPTY) { if (x2>=sheet2->dimx || y2>=sheet2->dimy || z2>=sheet2->dimz || SHEET(sheet2,x2,y2,z2)==(Cell*)0 || SHEET(sheet2,x2,y2,z2)->value.type==EMPTY) return 0; else return (sortkey&ASCENDING ? -1 : 1); } if (x2>=sheet2->dimx || y2>=sheet2->dimy || z2>=sheet2->dimz || SHEET(sheet2,x2,y2,z2)==(Cell*)0 || SHEET(sheet2,x2,y2,z2)->value.type==EMPTY) return (sortkey&ASCENDING ? 1 : -1); /*}}}*/ switch (SHEET(sheet1,x1,y1,z1)->value.type) { /* STRING */ /*{{{*/ case STRING: { if (SHEET(sheet2,x2,y2,z2)->value.type==STRING) { int r; r=strcmp(SHEET(sheet1,x1,y1,z1)->value.u.string,SHEET(sheet2,x2,y2,z2)->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; } /*}}}*/ /* FLOAT */ /*{{{*/ case FLOAT: { if (SHEET(sheet2,x2,y2,z2)->value.type==FLOAT) { if (SHEET(sheet1,x1,y1,z1)->value.u.fltvalue.u.flt) return (sortkey&ASCENDING ? -1 : 1); else if (SHEET(sheet1,x1,y1,z1)->value.u.flt==SHEET(sheet2,x2,y2,z2)->value.u.flt) return 0; else return (sortkey&ASCENDING ? 1 : -1); } if (SHEET(sheet2,x2,y2,z2)->value.type==INT) { if (SHEET(sheet1,x1,y1,z1)->value.u.fltvalue.u.integer) return (sortkey&ASCENDING ? -1 : 1); else if (SHEET(sheet1,x1,y1,z1)->value.u.flt==SHEET(sheet2,x2,y2,z2)->value.u.integer) return 0; else return (sortkey&ASCENDING ? 1 : -1); } return 2; } /*}}}*/ /* INT */ /*{{{*/ case INT: { if (SHEET(sheet2,x2,y2,z2)->value.type==INT) { if (SHEET(sheet1,x1,y1,z1)->value.u.integervalue.u.integer) return (sortkey&ASCENDING ? -1 : 1); else if (SHEET(sheet1,x1,y1,z1)->value.u.integer==SHEET(sheet2,x2,y2,z2)->value.u.integer) return 0; else return (sortkey&ASCENDING ? 1 : -1); } if (SHEET(sheet2,x2,y2,z2)->value.type==FLOAT) { if (SHEET(sheet1,x1,y1,z1)->value.u.integervalue.u.flt) return (sortkey&ASCENDING ? -1 : 1); else if (SHEET(sheet1,x1,y1,z1)->value.u.integer==SHEET(sheet2,x2,y2,z2)->value.u.flt) return 0; else return (sortkey&ASCENDING ? 1 : -1); } return 2; } /*}}}*/ default: return 2; } } /*}}}*/ /* resize -- check if sheet needs to be resized in any dimension */ /*{{{*/ void resize(Sheet *sheet, int x, int y, int z) { assert(x>=0); assert(y>=0); assert(z>=0); assert(sheet!=(Sheet*)0); if (x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz) { /* variables */ /*{{{*/ Cell **newsheet; int *newcolumn; unsigned int ndimx,ndimy,ndimz; /*}}}*/ sheet->changed=1; ndimx=(x>=sheet->dimx ? x+1 : sheet->dimx); ndimy=(y>=sheet->dimy ? y+1 : sheet->dimy); ndimz=(z>=sheet->dimz ? z+1 : sheet->dimz); /* allocate new sheet */ /*{{{*/ newsheet=malloc(ndimx*ndimy*ndimz*sizeof(Cell*)); for (x=0; xdimx && ydimy && zdimz) *(newsheet+x*ndimz*ndimy+y*ndimz+z)=SHEET(sheet,x,y,z); else *(newsheet+x*ndimz*ndimy+y*ndimz+z)=(Cell*)0; } if (sheet->sheet!=(Cell**)0) free(sheet->sheet); sheet->sheet=newsheet; /*}}}*/ /* allocate new columns */ /*{{{*/ if (x>sheet->dimx || z>=sheet->dimz) { newcolumn=malloc(ndimx*ndimz*sizeof(int)); for (x=0; xdimx && zdimz) *(newcolumn+x*ndimz+z)=*(sheet->column+x*sheet->dimz+z); else *(newcolumn+x*ndimz+z)=DEF_COLUMNWIDTH; } if (sheet->column!=(int*)0) free(sheet->column); sheet->column=newcolumn; } /*}}}*/ sheet->dimx=ndimx; sheet->dimy=ndimy; sheet->dimz=ndimz; } } /*}}}*/ /* initcell -- initialise new cell, if it does not exist yet */ /*{{{*/ void initcell(Sheet *sheet, int x, int y, int z) { assert(x>=0); assert(y>=0); assert(z>=0); resize(sheet,x,y,z); if (SHEET(sheet,x,y,z)==(Cell*)0) { sheet->changed=1; SHEET(sheet,x,y,z)=malloc(sizeof(Cell)); SHEET(sheet,x,y,z)->contents=(Token**)0; SHEET(sheet,x,y,z)->ccontents=(Token**)0; SHEET(sheet,x,y,z)->label=(char*)0; SHEET(sheet,x,y,z)->adjust=AUTOADJUST; SHEET(sheet,x,y,z)->precision=-1; SHEET(sheet,x,y,z)->shadowed=0; SHEET(sheet,x,y,z)->bold=0; SHEET(sheet,x,y,z)->underline=0; SHEET(sheet,x,y,z)->scientific=DEF_SCIENTIFIC; SHEET(sheet,x,y,z)->value.type=EMPTY; SHEET(sheet,x,y,z)->resvalue.type=EMPTY; SHEET(sheet,x,y,z)->locked=0; SHEET(sheet,x,y,z)->ignored=0; SHEET(sheet,x,y,z)->clock_t0=0; SHEET(sheet,x,y,z)->clock_t1=0; SHEET(sheet,x,y,z)->clock_t2=0; } } /*}}}*/ /* cachelabels -- create new label cache */ /*{{{*/ void cachelabels(Sheet *sheet) { int i,x,y,z; if (sheet==(Sheet*)0) return; for (i=0; ilabelcache[i]; run!=(struct Label*)0;) { struct Label *runnext; runnext=run->next; free(run); run=runnext; } sheet->labelcache[i]=(struct Label*)0; } /*}}}*/ for (x=0; xdimx; ++x) for (y=0; ydimy; ++y) for (z=0; zdimz; ++z) /* cache all labels */ /*{{{*/ { const char *l; l=getlabel(sheet,x,y,z); if (*l) { unsigned long hx; struct Label **run; HASH(hx,l); for (run=&sheet->labelcache[(unsigned int)hx]; *run!=(struct Label*)0 && strcmp(l,(*run)->label); run=&((*run)->next)); if (*run==(struct Label*)0) { *run=malloc(sizeof(struct Label)); (*run)->next=(struct Label*)0; (*run)->label=l; (*run)->x=x; (*run)->y=y; (*run)->z=z; } /* else we have a duplicate label, which _can_ happen under */ /* unfortunate conditions. Don't tell anybody. */ } } /*}}}*/ } /*}}}*/ /* freesheet -- free all cells of an entire spread sheet */ /*{{{*/ void freesheet(Sheet *sheet, int all) { /* variables */ /*{{{*/ int x,y,z; /*}}}*/ assert(sheet!=(Sheet*)0); sheet->changed=0; for (x=0; xdimx; ++x) for (y=0; ydimy; ++y) for (z=0; zdimz; ++z) { freecell(sheet,x,y,z); } if (all) { int i; for (i=0; ilabelcache[i]; run!=(struct Label*)0;) { struct Label *runnext; runnext=run->next; free(run); run=runnext; } } /*}}}*/ if (sheet->sheet) free(sheet->sheet); if (sheet->column) free(sheet->column); if (sheet->name) free(sheet->name); } else { for (x=0; xdimx; ++x) for (z=0; zdimz; ++z) { *(sheet->column+x*sheet->dimz+z)=DEF_COLUMNWIDTH; } cachelabels(sheet); forceupdate(sheet); } } /*}}}*/ /* forceupdate -- clear all clock and update flags */ /*{{{*/ void forceupdate(Sheet *sheet) { int i; assert(sheet!=(Sheet*)0); for (i=0; idimx*sheet->dimy*sheet->dimz; ++i) if (*(sheet->sheet+i)!=(Cell*)0) { (*(sheet->sheet+i))->updated=0; (*(sheet->sheet+i))->clock_t0=0; (*(sheet->sheet+i))->clock_t1=0; (*(sheet->sheet+i))->clock_t2=0; } update(sheet); } /*}}}*/ /* freecell -- free one cell */ /*{{{*/ void freecell(Sheet *sheet, int x, int y, int z) { assert(sheet!=(Sheet*)0); if (sheet->sheet!=(Cell**)0 && xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0) { tvecfree(SHEET(sheet,x,y,z)->contents); tvecfree(SHEET(sheet,x,y,z)->ccontents); tfree(&(SHEET(sheet,x,y,z)->value)); free(SHEET(sheet,x,y,z)); SHEET(sheet,x,y,z)=(Cell*)0; sheet->changed=1; } } /*}}}*/ /* columnwidth -- get width of column */ /*{{{*/ int columnwidth(Sheet *sheet, int x, int z) { assert(sheet!=(Sheet*)0); if (xdimx && zdimz) return (*(sheet->column+x*sheet->dimz+z)); else return DEF_COLUMNWIDTH; } /*}}}*/ /* setwidth -- set width of column */ /*{{{*/ void setwidth(Sheet *sheet, int x, int z, int width) { assert(sheet!=(Sheet*)0); resize(sheet,x,1,z); sheet->changed=1; *(sheet->column+x*sheet->dimz+z)=width; } /*}}}*/ /* cellwidth -- get width of a cell */ /*{{{*/ int cellwidth(Sheet *sheet, int x, int y, int z) { int width; if (SHADOWED(sheet,x,y,z)) return 0; width=columnwidth(sheet,x,z); for (++x; SHADOWED(sheet,x,y,z); width+=columnwidth(sheet,x,z),++x); return width; } /*}}}*/ /* putcont -- assign new contents */ /*{{{*/ void putcont(Sheet *sheet, int x, int y, int z, Token **t, int c) { assert(sheet!=(Sheet*)0); sheet->changed=1; resize(sheet,x,y,z); initcell(sheet,x,y,z); if (c) { tvecfree(SHEET(sheet,x,y,z)->ccontents); SHEET(sheet,x,y,z)->ccontents=t; } else { tvecfree(SHEET(sheet,x,y,z)->contents); SHEET(sheet,x,y,z)->contents=t; } redraw_cell(sheet, x, y, z); } /*}}}*/ /* getcont -- get contents */ /*{{{*/ Token **getcont(Sheet *sheet, int x, int y, int z, int c) { if (x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz) return (Token**)0; else if (SHEET(sheet,x,y,z)==(Cell*)0) return (Token**)0; else if (c==2) return (SHEET(sheet,x,y,z)->clock_t0 && SHEET(sheet,x,y,z)->ccontents ? SHEET(sheet,x,y,z)->ccontents : SHEET(sheet,x,y,z)->contents); else return (c ? SHEET(sheet,x,y,z)->ccontents : SHEET(sheet,x,y,z)->contents); } /*}}}*/ /* getvalue -- get tcopy()ed value */ /*{{{*/ Token getvalue(Sheet *sheet, int x, int y, int z) { /* variables */ /*{{{*/ Token result; int orig_upd_clock; /*}}}*/ assert(sheet!=(Sheet*)0); if (x<0 || y<0 || z<0) /* return error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Negative index")); } /*}}}*/ else if (getcont(sheet,x,y,z,2)==(Token**)0) /* return empty value */ /*{{{*/ result.type=EMPTY; /*}}}*/ else /* update value of this cell if needed and return it */ /*{{{*/ { orig_upd_clock=upd_clock; if (SHEET(sheet,x,y,z)->ignored) { /* variables */ /*{{{*/ Token oldvalue; /*}}}*/ oldvalue=SHEET(sheet,x,y,z)->value; SHEET(sheet,x,y,z)->updated=1; SHEET(sheet,x,y,z)->value.type=EMPTY; tfree(&oldvalue); } else if (SHEET(sheet,x,y,z)->updated==0) { /* variables */ /*{{{*/ Sheet *old_sheet; int old_x,old_y,old_z,old_max_eval; Token oldvalue; /*}}}*/ old_sheet=upd_sheet; old_x=upd_x; old_y=upd_y; old_z=upd_z; old_max_eval=max_eval; upd_sheet=sheet; upd_x=x; upd_y=y; upd_z=z; max_eval=MAX_EVALNEST; if (SHEET(sheet,x,y,z)->clock_t1==0) { SHEET(sheet,x,y,z)->updated=1; oldvalue=SHEET(sheet,x,y,z)->value; upd_clock=0; SHEET(sheet,x,y,z)->value=eval(getcont(sheet,x,y,z,2)); tfree(&oldvalue); } else if (upd_clock) { SHEET(sheet,x,y,z)->updated=1; upd_clock=0; SHEET(sheet,x,y,z)->resvalue=eval(getcont(sheet,x,y,z,2)); } upd_sheet=old_sheet; upd_x=old_x; upd_y=old_y; upd_z=old_z; max_eval=old_max_eval; } if (!orig_upd_clock) result=tcopy(SHEET(sheet,x,y,z)->value); } /*}}}*/ return result; } /*}}}*/ /* update -- update all cells that need it */ /*{{{*/ void update(Sheet *sheet) { int x,y,z,kp,iterating; assert(sheet!=(Sheet*)0); kp=0; iterating=0; do { sheet->clk=0; if (iterating==1) { line_msg((const char*)0,_("Calculating running, press Escape to abort it")); ++iterating; } else if (iterating==0) ++iterating; for (x=0; xdimx; ++x) for (y=0; ydimy; ++y) for (z=0; zdimz; ++z) { if (SHEET(sheet,x,y,z) && SHEET(sheet,x,y,z)->clock_t2) { SHEET(sheet,x,y,z)->updated=0; SHEET(sheet,x,y,z)->clock_t0=1; SHEET(sheet,x,y,z)->clock_t1=1; SHEET(sheet,x,y,z)->clock_t2=0; } } for (x=0; xdimx; ++x) for (y=0; ydimy; ++y) for (z=0; zdimz; ++z) { upd_clock=1; getvalue(sheet,x,y,z); } for (x=0; xdimx; ++x) for (y=0; ydimy; ++y) for (z=0; zdimz; ++z) { if (SHEET(sheet,x,y,z) && SHEET(sheet,x,y,z)->clock_t1) { tfree(&(SHEET(sheet,x,y,z)->value)); SHEET(sheet,x,y,z)->value=SHEET(sheet,x,y,z)->resvalue; SHEET(sheet,x,y,z)->clock_t1=0; } } upd_clock=0; } while (sheet->clk && !(kp=keypressed())); if (iterating==2) line_msg((const char*)0,kp ? _("Calculation aborted") : _("Calculation finished")); sheet->clk=0; redraw_sheet(sheet); } /*}}}*/ /* geterror -- get malloc()ed error string */ /*{{{*/ char *geterror(Sheet *sheet, int x, int y, int z) { Token v; assert(sheet!=(Sheet*)0); if ((v=getvalue(sheet,x,y,z)).type!=EEK) { tfree(&v); return (char*)0; } 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, int x, int y, int z) { Token *tv[2],t; assert(sheet!=(Sheet*)0); t=getvalue(sheet,x,y,z); tv[0]=&t; tv[1]=(Token*)0; print(s,size,chars,quote,scientific,precision,tv); tfree(&t); } /*}}}*/ /* getadjust -- get cell adjustment */ /*{{{*/ Adjust getadjust(Sheet *sheet, int x, int y, int z) { assert(sheet!=(Sheet*)0); if (x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz || SHEET(sheet,x,y,z)==(Cell*)0) { return LEFT; } else if (SHEET(sheet,x,y,z)->adjust==AUTOADJUST) return (SHEET(sheet,x,y,z)->value.type==INT || SHEET(sheet,x,y,z)->value.type==FLOAT ? RIGHT : LEFT); else return (SHEET(sheet,x,y,z)->adjust); } /*}}}*/ /* setadjust -- set cell adjustment */ /*{{{*/ void setadjust(Sheet *sheet, int x, int y, int z, Adjust adjust) { assert(sheet!=(Sheet*)0); sheet->changed=1; resize(sheet,x,y,z); initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->adjust=adjust; } /*}}}*/ /* shadow -- shadow cell by left neighbour */ /*{{{*/ void shadow(Sheet *sheet, int x, int y, int z, int yep) { sheet->changed=1; initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->shadowed=yep; } /*}}}*/ /* shadowed -- is cell shadowed? */ /*{{{*/ int shadowed(Sheet *sheet, int x, int y, int z) { return (SHADOWED(sheet,x,y,z)); } /*}}}*/ /* bold -- bold font */ /*{{{*/ void bold(Sheet *sheet, int x, int y, int z, int yep) { sheet->changed=1; initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->bold=yep; } /*}}}*/ /* isbold -- is cell bold? */ /*{{{*/ int isbold(Sheet *sheet, int x, int y, int z) { return (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->bold); } /*}}}*/ /* underline -- underline */ /*{{{*/ void underline(Sheet *sheet, int x, int y, int z, int yep) { sheet->changed=1; initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->underline=yep; } /*}}}*/ /* isunderline -- is cell underlined? */ /*{{{*/ int underlined(Sheet *sheet, int x, int y, int z) { return (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->underline); } /*}}}*/ /* lockcell -- lock cell */ /*{{{*/ void lockcell(Sheet *sheet, int x, int y, int z, int yep) { sheet->changed=1; initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->locked=yep; } /*}}}*/ /* locked -- is cell locked? */ /*{{{*/ int locked(Sheet *sheet, int x, int y, int z) { return (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->locked); } /*}}}*/ /* transparent -- is cell transparent? */ /*{{{*/ int transparent(Sheet *sheet, int x, int y, int z) { return (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->transparent); } /*}}}*/ /* maketrans -- make cell transparent */ /*{{{*/ void maketrans(Sheet *sheet, int x, int y, int z, int yep) { sheet->changed=1; initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->transparent=yep; } /*}}}*/ /* igncell -- ignore cell */ /*{{{*/ void igncell(Sheet *sheet, int x, int y, int z, int yep) { sheet->changed=1; initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->ignored=yep; } /*}}}*/ /* ignored -- is cell ignored? */ /*{{{*/ int ignored(Sheet *sheet, int x, int y, int z) { return (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->ignored); } /*}}}*/ /* clk -- clock cell */ /*{{{*/ void clk(Sheet *sheet, int x, int y, int z) { assert(sheet!=(Sheet*)0); assert(x>=0 && xdimx); assert(y>=0 && ydimy); assert(z>=0 && zdimz); if (SHEET(sheet,x,y,z)) { SHEET(sheet,x,y,z)->clock_t2=1; sheet->clk=1; } } /*}}}*/ /* setscientific -- cell value should be displayed in scientific notation */ /*{{{*/ void setscientific(Sheet *sheet, int x, int y, int z, int yep) { sheet->changed=1; resize(sheet,x,y,z); initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->scientific=yep; } /*}}}*/ /* getscientific -- should value be displayed in scientific notation? */ /*{{{*/ int getscientific(Sheet *sheet, int x, int y, int z) { if (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0) return SHEET(sheet,x,y,z)->scientific; else return DEF_SCIENTIFIC; } /*}}}*/ /* setprecision -- set cell precision */ /*{{{*/ void setprecision(Sheet *sheet, int x, int y, int z, int precision) { sheet->changed=1; resize(sheet,x,y,z); initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->precision=precision; } /*}}}*/ /* getprecision -- get cell precision */ /*{{{*/ int getprecision(Sheet *sheet, int x, int y, int z) { if (xdimx && ydimy && zdimz && SHEET(sheet,x,y,z)!=(Cell*)0) return (SHEET(sheet,x,y,z)->precision==-1 ? def_precision : SHEET(sheet,x,y,z)->precision); else return def_precision; } /*}}}*/ /* getlabel -- get cell label */ /*{{{*/ const char *getlabel(Sheet *sheet, int x, int y, int z) { if (x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz || SHEET(sheet,x,y,z)==(Cell*)0 || SHEET(sheet,x,y,z)->label==(char*)0) return ""; else return (SHEET(sheet,x,y,z)->label); } /*}}}*/ /* setlabel -- set cell label */ /*{{{*/ void setlabel(Sheet *sheet, int x, int y, int z, const char *buf, int update) { sheet->changed=1; resize(sheet,x,y,z); initcell(sheet,x,y,z); if (SHEET(sheet,x,y,z)->label!=(char*)0) free(SHEET(sheet,x,y,z)->label); if (*buf!='\0') SHEET(sheet,x,y,z)->label=strcpy(malloc(strlen(buf)+1),buf); else SHEET(sheet,x,y,z)->label=(char*)0; if (update) { cachelabels(sheet); forceupdate(sheet); } } /*}}}*/ /* findlabel -- return cell location for a given label */ /*{{{*/ Token findlabel(Sheet *sheet, const char *label) { /* variables */ /*{{{*/ Token result; unsigned long hx; struct Label *run; /*}}}*/ assert(sheet!=(Sheet*)0); /* if (sheet==(Sheet*)0) run=(struct Label*)0; else */ { HASH(hx,label); for (run=sheet->labelcache[(unsigned int)hx]; run!=(struct Label*)0 && strcmp(label,run->label); run=run->next); } if (run) { result.type=LOCATION; result.u.location[0]=run->x; result.u.location[1]=run->y; result.u.location[2]=run->z; } else { result.type=EEK; result.u.err=mystrmalloc(_("No such label")); } return result; } /*}}}*/ /* relabel -- search and replace for labels */ /*{{{*/ void relabel(Sheet *sheet, const char *oldlabel, const char *newlabel, int x, int y, int z) { /* variables */ /*{{{*/ Token **run; /*}}}*/ /* asserts */ /*{{{*/ assert(sheet!=(Sheet*)0); assert(oldlabel!=(const char*)0); assert(newlabel!=(const char*)0); assert(x>=0); assert(y>=0); assert(z>=0); /*}}}*/ if (!(x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz || SHEET(sheet,x,y,z)==(Cell*)0 || SHEET(sheet,x,y,z)->contents==(Token**)0)) { for (run=SHEET(sheet,x,y,z)->contents; *run!=(Token*)0; ++run) { if ((*run)->type==LIDENT && strcmp((*run)->u.lident,oldlabel)==0) { free((*run)->u.lident); (*run)->u.lident=mystrmalloc(newlabel); } } } if (!(x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz || SHEET(sheet,x,y,z)==(Cell*)0 || SHEET(sheet,x,y,z)->ccontents==(Token**)0)) { for (run=SHEET(sheet,x,y,z)->ccontents; *run!=(Token*)0; ++run) { if ((*run)->type==LIDENT && strcmp((*run)->u.lident,oldlabel)==0) { free((*run)->u.lident); (*run)->u.lident=mystrmalloc(newlabel); } } } cachelabels(sheet); forceupdate(sheet); } /*}}}*/ /* 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 (SHEET(sheet,x,y,z)!=(Cell*)0) { 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,SHEET(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, int x1, int y1, int z1, int x2, int y2, int z2, unsigned int *count) { /* variables */ /*{{{*/ FILE *fp=(FILE*)0; /* cause run time error */ int x,y,z; char buf[1024]; char num[20]; char fullname[PATH_MAX]; /*}}}*/ /* asserts */ /*{{{*/ assert(sheet!=(Sheet*)0); assert(name!=(const char*)0); /*}}}*/ *count=0; for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) if (shadowed(sheet,x1,y,z)) return _("Shadowed cells in first column"); if (!body && (fp=fopen(name,"w"))==(FILE*)0) return strerror(errno); for (z=z1; z<=z2; ++z) { if (body) /* open new file */ /*{{{*/ { sprintf(num,".%d",z); fullname[sizeof(fullname)-strlen(num)-1]='\0'; (void)strncpy(fullname,name,sizeof(fullname)-strlen(num)-1); fullname[sizeof(fullname)-1]='\0'; (void)strncat(fullname,num,sizeof(fullname)-strlen(num)-1); fullname[sizeof(fullname)-1]='\0'; if ((fp=fopen(fullname,"w"))==(FILE*)0) return strerror(errno); } /*}}}*/ else if (fputs_close(".TS\n",fp)==EOF) return strerror(errno); for (y=y1; y<=y2; ++y) { /* print format */ /*{{{*/ if (y>y1 && fputs_close(".T&\n",fp)==EOF) return strerror(errno); for (x=x1; x<=x2; ++x) { if (x>x1 && fputc_close(' ',fp)==EOF) return strerror(errno); if (shadowed(sheet,x,y,z)) { if (fputc_close('s',fp)==EOF) return strerror(errno); } if (isbold(sheet,x,y,z)) { if (fputc_close('b',fp)==EOF) return strerror(errno); } if (underlined(sheet,x,y,z)) { if (fputc_close('u',fp)==EOF) return strerror(errno); } else switch (getadjust(sheet,x,y,z)) { case LEFT: if (fputc_close('l',fp)==EOF) return strerror(errno); break; case RIGHT: if (fputc_close('r',fp)==EOF) return strerror(errno); break; case CENTER: if (fputc_close('c',fp)==EOF) return strerror(errno); break; default: assert(0); } } if (fputs_close(".\n",fp)==EOF) return strerror(errno); /*}}}*/ /* print contents */ /*{{{*/ for (x=x1; x<=x2; ++x) { if (!shadowed(sheet,x,y,z)) { if (x>x1 && fputc_close('\t',fp)==EOF) return strerror(errno); if (SHEET(sheet,x,y,z)!=(Cell*)0) { char *bufp; printvalue(buf,sizeof(buf),0,0,getscientific(sheet,x,y,z),getprecision(sheet,x,y,z),sheet,x,y,z); if (transparent(sheet,x,y,z)) { if (fputs_close(buf,fp)==EOF) return strerror(errno); } else for (bufp=buf; *bufp; ++bufp) switch (*bufp) { case '\\': { if (fputc_close('\\',fp)==EOF || fputc_close('e',fp)==EOF) return strerror(errno); break; } case '_': { if (fputc_close('\\',fp)==EOF || fputc_close('&',fp)==EOF || fputc_close('_',fp)==EOF) return strerror(errno); break; } case '.': { if (x==x1 && bufp==buf && (fputc_close('\\',fp)==EOF || fputc_close('&',fp)==EOF)) return strerror(errno); if (fputc_close('.',fp)==EOF) return strerror(errno); break; } case '\'': { if (x==x1 && bufp==buf && (fputc_close('\\',fp)==EOF || fputc_close('&',fp)==EOF)) return strerror(errno); if (fputc_close('\'',fp)==EOF) return strerror(errno); break; } case '-': { if (*(bufp+1)=='-') { if (fputc_close('-',fp)==EOF) return strerror(errno); else ++bufp; } else if (fputs_close("\\-",fp)==EOF) return strerror(errno); break; } default: if (fputc_close(*bufp,fp)==EOF) return strerror(errno); } } } } if (fputc_close('\n',fp)==EOF) return strerror(errno); /*}}}*/ ++*count; } if (!body) { if (fputs_close(".TE\n",fp)==EOF) return strerror(errno); if (zx1) if (fputc_close(sep,fp)==EOF) return strerror(errno); if (SHEET(sheet,x,y,z)!=(Cell*)0) { char *buf,*s; buf=malloc(255*UTF8SZ+1); printvalue(buf,255*UTF8SZ+1,255,0,getscientific(sheet,x,y,z),getprecision(sheet,x,y,z),sheet,x,y,z); if (SHEET(sheet,x,y,z)->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 (SHEET(sheet,x,y,z)->value.type==STRING && fputc_close('"',fp)==EOF) return strerror(errno); } ++*count; } if (fputc_close('\n',fp)==EOF) return strerror(errno); } if (zdimz-1; z>=0; --z) { for (y=sheet->dimy-1; y>=0; --y) { for (x=sheet->dimx-1; x>=0; --x) { if (y==0) if (columnwidth(sheet,x,z)!=DEF_COLUMNWIDTH) fprintf(fp,"W%d %d %d\n",x,z,columnwidth(sheet,x,z)); if (SHEET(sheet,x,y,z)!=(Cell*)0) { fprintf(fp,"C%d %d %d ",x,y,z); if (SHEET(sheet,x,y,z)->adjust!=AUTOADJUST) fprintf(fp,"A%c ","lrc"[SHEET(sheet,x,y,z)->adjust]); if (SHEET(sheet,x,y,z)->label) fprintf(fp,"L%s ",SHEET(sheet,x,y,z)->label); if (SHEET(sheet,x,y,z)->precision!=-1) fprintf(fp,"P%d ",SHEET(sheet,x,y,z)->precision); if (SHEET(sheet,x,y,z)->shadowed) fprintf(fp,"S "); if (SHEET(sheet,x,y,z)->bold) fprintf(fp,"B "); if (SHEET(sheet,x,y,z)->underline) fprintf(fp,"U "); if (SHEET(sheet,x,y,z)->scientific!=DEF_SCIENTIFIC) fprintf(fp,"E "); if (SHEET(sheet,x,y,z)->locked) fprintf(fp,"C "); if (SHEET(sheet,x,y,z)->transparent) fprintf(fp,"T "); if (SHEET(sheet,x,y,z)->contents) { char buf[4096]; if (fputc_close(':',fp)==EOF) return strerror(errno); print(buf,sizeof(buf),0,1,SHEET(sheet,x,y,z)->scientific,SHEET(sheet,x,y,z)->precision,SHEET(sheet,x,y,z)->contents); if (fputs_close(buf,fp)==EOF) return strerror(errno); } if (SHEET(sheet,x,y,z)->ccontents) { char buf[4096]; if (fputs_close("\\\n",fp)==EOF) return strerror(errno); print(buf,sizeof(buf),0,1,SHEET(sheet,x,y,z)->scientific,SHEET(sheet,x,y,z)->precision,SHEET(sheet,x,y,z)->ccontents); if (fputs_close(buf,fp)==EOF) return strerror(errno); } if (fputc_close('\n',fp)==EOF) return strerror(errno); ++*count; } } } } if (fclose(fp)==EOF) return strerror(errno); return (const char*)0; } /*}}}*/ /* loadport -- load from portable text */ /*{{{*/ const char *loadport(Sheet *sheet, const char *name) { /* variables */ /*{{{*/ static char errbuf[80]; FILE *fp; int x,y,z; char buf[4096]; int line; const char *ns,*os; const char *err; int precision; char *label; Adjust adjust; int shadowed; int bold; int underline; int scientific; int locked; int transparent; int ignored; Token **contents,**ccontents; int width; /*}}}*/ if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno); freesheet(sheet,0); err=(const char*)0; line=1; while (fgets(buf,sizeof(buf),fp)!=(char*)0) { /* remove nl */ /*{{{*/ width=strlen(buf); if (width>0 && buf[width-1]=='\n') buf[--width]='\0'; /*}}}*/ switch (buf[0]) { /* C -- parse cell */ /*{{{*/ case 'C': { int cc=0; if (width>0 && buf[width-1]=='\\') { buf[--width]='\0'; cc=1; } adjust=AUTOADJUST; precision=-1; label=(char*)0; contents=(Token**)0; ccontents=(Token**)0; shadowed=0; bold=0; underline=0; scientific=DEF_SCIENTIFIC; locked=0; transparent=0; ignored=0; /* parse x y and z */ /*{{{*/ os=ns=buf+1; x=posnumber(os,&ns); if (os==ns) { sprintf(errbuf,_("Parse error for x position in line %d"),line); err=errbuf; goto eek; } while (*ns==' ') ++ns; os=ns; y=posnumber(os,&ns); if (os==ns) { sprintf(errbuf,_("Parse error for y position in line %d"),line); err=errbuf; goto eek; } while (*ns==' ') ++ns; os=ns; z=posnumber(os,&ns); if (os==ns) { sprintf(errbuf,_("Parse error for z position in line %d"),line); err=errbuf; goto eek; } /*}}}*/ /* parse optional attributes */ /*{{{*/ do { while (*ns==' ') ++ns; switch (*ns) { /* A -- adjustment */ /*{{{*/ case 'A': { ++ns; switch (*ns) { case 'l': adjust=LEFT; ++ns; break; case 'r': adjust=RIGHT; ++ns; break; case 'c': adjust=CENTER; ++ns; break; default: sprintf(errbuf,_("Parse error for adjustment in line %d"),line); err=errbuf; goto eek; } break; } /*}}}*/ /* L -- label */ /*{{{*/ case 'L': { char buf[1024],*p; p=buf; ++ns; while (*ns && *ns!=' ') { *p=*ns; ++p; ++ns; } *p='\0'; label=mystrmalloc(buf); break; } /*}}}*/ /* P -- precision */ /*{{{*/ case 'P': { os=++ns; precision=posnumber(os,&ns); if (os==ns) { sprintf(errbuf,_("Parse error for precision in line %d"),line); err=errbuf; goto eek; } break; } /*}}}*/ /* S -- shadowed */ /*{{{*/ case 'S': { if (x==0) { sprintf(errbuf,_("Trying to shadow cell (%d,%d,%d) in line %d"),x,y,z,line); err=errbuf; goto eek; } ++ns; shadowed=1; break; } /*}}}*/ /* U -- underline */ /*{{{*/ case 'U': { if (x==0) { sprintf(errbuf,_("Trying to underline cell (%d,%d,%d) in line %d"),x,y,z,line); err=errbuf; goto eek; } ++ns; underline=1; break; } /*}}}*/ /* B -- bold */ /*{{{*/ case 'B': { if (x==0) { sprintf(errbuf,_("Trying to bold cell (%d,%d,%d) in line %d"),x,y,z,line); err=errbuf; goto eek; } ++ns; bold=1; break; } /*}}}*/ /* E -- scientific */ /*{{{*/ case 'E': { ++ns; scientific=1; break; } /*}}}*/ /* O -- locked */ /*{{{*/ case 'O': { ++ns; locked=1; break; } /*}}}*/ /* T -- transparent */ /*{{{*/ case 'T': { ++ns; transparent=1; break; } /*}}}*/ /* I -- ignored */ /*{{{*/ case 'I': { ++ns; ignored=1; break; } /*}}}*/ /* : \0 -- do nothing */ /*{{{*/ case ':': case '\0': break; /*}}}*/ /* default -- error */ /*{{{*/ default: sprintf(errbuf,_("Invalid option %c in line %d"),*ns,line); err=errbuf; goto eek; /*}}}*/ } } while (*ns!=':' && *ns!='\0'); /*}}}*/ /* convert remaining string into token sequence */ /*{{{*/ if (*ns) { ++ns; contents=scan(&ns); if (contents==(Token**)0) { tvecfree(contents); sprintf(errbuf,_("Expression syntax error in line %d"),line); err=errbuf; goto eek; } } /*}}}*/ /* convert remaining string into token sequence */ /*{{{*/ if (cc && fgets(buf,sizeof(buf),fp)!=(char*)0) { ++line; /* remove nl */ /*{{{*/ width=strlen(buf); if (width>0 && buf[width-1]=='\n') buf[width-1]='\0'; /*}}}*/ ns=buf; ccontents=scan(&ns); if (ccontents==(Token**)0) { tvecfree(ccontents); sprintf(errbuf,_("Expression syntax error in line %d"),line); err=errbuf; goto eek; } } /*}}}*/ initcell(sheet,x,y,z); SHEET(sheet,x,y,z)->adjust=adjust; SHEET(sheet,x,y,z)->label=label; SHEET(sheet,x,y,z)->precision=precision; SHEET(sheet,x,y,z)->shadowed=shadowed; SHEET(sheet,x,y,z)->bold=bold; SHEET(sheet,x,y,z)->underline=underline; SHEET(sheet,x,y,z)->scientific=scientific; SHEET(sheet,x,y,z)->locked=locked; SHEET(sheet,x,y,z)->transparent=transparent; SHEET(sheet,x,y,z)->ignored=ignored; SHEET(sheet,x,y,z)->contents=contents; SHEET(sheet,x,y,z)->ccontents=ccontents; break; } /*}}}*/ /* W -- column width */ /*{{{*/ case 'W': { /* parse x and z */ /*{{{*/ os=ns=buf+1; x=posnumber(os,&ns); if (os==ns) { sprintf(errbuf,_("Parse error for x position in line %d"),line); err=errbuf; goto eek; } while (*ns==' ') ++ns; os=ns; z=posnumber(os,&ns); if (os==ns) { sprintf(errbuf,_("Parse error for z position in line %d"),line); err=errbuf; goto eek; } /*}}}*/ /* parse width */ /*{{{*/ while (*ns==' ') ++ns; os=ns; width=posnumber(os,&ns); if (os==ns) { sprintf(errbuf,_("Parse error for width in line %d"),line); err=errbuf; goto eek; } /*}}}*/ setwidth(sheet,x,z,width); break; } /*}}}*/ /* # -- comment */ /*{{{*/ case '#': break; /*}}}*/ /* default -- error */ /*{{{*/ default: { sprintf(errbuf,_("Unknown tag %c in line %d"),buf[0],line); err=errbuf; goto eek; } /*}}}*/ } ++line; } eek: if (fclose(fp)==EOF && err==(const char*)0) err=strerror(errno); sheet->changed=0; cachelabels(sheet); forceupdate(sheet); return err; } /*}}}*/ /* loadxdr -- load a spread sheet in XDR */ /*{{{*/ const char *loadxdr(Sheet *sheet, const char *name) { /* variables */ /*{{{*/ FILE *fp; XDR xdrs; int x,y,z; int width; int u; int olderror; /*}}}*/ 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,&x,&z,&width)==0) { olderror=errno; xdr_destroy(&xdrs); (void)fclose(fp); return strerror(olderror); } setwidth(sheet,x,z,width); break; } /*}}}*/ /* 1 -- cell element */ /*{{{*/ case 1: { if (xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0) { olderror=errno; xdr_destroy(&xdrs); (void)fclose(fp); return strerror(olderror); } initcell(sheet,x,y,z); if (xdr_cell(&xdrs,SHEET(sheet,x,y,z))==0) { freecell(sheet,x,y,z); 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) { /* variables */ /*{{{*/ FILE *fp; Token **t; const char *err; int line,x; char ln[4096]; const char *str; double value; long lvalue; int separator = 0; /*}}}*/ if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno); err=(const char*)0; for (x=0,line=1; fgets(ln,sizeof(ln),fp); ++line) { const char *s; const char *cend; if (!separator) { /* FIXME: find a better way to autodetect */ int ccnt = 0, scnt = 0; char *pos = ln; while ((pos = strchr(pos, ','))) pos++, ccnt++; pos = ln; while ((pos = strchr(pos, ';'))) pos++, scnt++; if (ccnt || scnt) separator = 1; csv_setopt(scnt > ccnt); } s=cend=ln; x=0; 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 */ /*{{{*/ { t[0]->type=INT; t[0]->u.integer=lvalue; putcont(sheet, sheet->curx+x, sheet->cury+line-1, sheet->curz, t, 0); } /*}}}*/ else { value=csv_double(s,&cend); if (s!=cend) /* ok, it is a double */ /*{{{*/ { t[0]->type=FLOAT; t[0]->u.flt=value; putcont(sheet, sheet->curx+x, sheet->cury+line-1, sheet->curz, t, 0); } /*}}}*/ else { str=csv_string(s,&cend); if (s!=cend) /* ok, it is a string */ /*{{{*/ { t[0]->type=STRING; t[0]->u.string=mystrmalloc(str); putcont(sheet, sheet->curx+x, sheet->cury+line-1, sheet->curz, t, 0); } /*}}}*/ else { tvecfree(t); csv_separator(s,&cend); while (s==cend && *s && *s!='\n') { err=_("unknown values ignored"); csv_separator(++s,&cend); } /* else it is nothing, which does not need to be stored :) */ } } } } while (s!=cend ? s=cend,++x,1 : 0); } fclose(fp); return err; } /*}}}*/ /* insertcube -- insert a block */ /*{{{*/ void insertcube(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction ins) { /* variables */ /*{{{*/ int x,y,z; /*}}}*/ switch (ins) { /* IN_X */ /*{{{*/ case IN_X: { int right; right=sheet->dimx+x2-x1; for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) for (x=right; x>x2; --x) { resize(sheet,x,y,z); SHEET(sheet,x,y,z)=SHEET(sheet,x-(x2-x1+1),y,z); SHEET(sheet,x-(x2-x1+1),y,z)=(Cell*)0; } break; } /*}}}*/ /* IN_Y */ /*{{{*/ case IN_Y: { int down; down=sheet->dimy+y2-y1; for (z=z1; z<=z2; ++z) for (x=x1; x<=x2; ++x) for (y=down; y>y2; --y) { resize(sheet,x,y,z); SHEET(sheet,x,y,z)=SHEET(sheet,x,y-(y2-y1+1),z); SHEET(sheet,x,y-(y2-y1+1),z)=(Cell*)0; } break; } /*}}}*/ /* IN_Z */ /*{{{*/ case IN_Z: { int bottom; bottom=sheet->dimz+z2-z1; for (y=y1; y<=y2; ++y) for (x=x1; x<=x2; ++x) for (z=bottom; z>z2; --z) { resize(sheet,x,y,z); SHEET(sheet,x,y,z)=SHEET(sheet,x,y,z-(z2-z1+1)); SHEET(sheet,x,y,z-(z2-z1+1))=(Cell*)0; } break; } /*}}}*/ /* default */ /*{{{*/ default: assert(0); /*}}}*/ } sheet->changed=1; cachelabels(sheet); forceupdate(sheet); } /*}}}*/ /* deletecube -- delete a block */ /*{{{*/ void deletecube(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction del) { /* variables */ /*{{{*/ int x,y,z; /*}}}*/ /* free cells in marked block */ /*{{{*/ for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) freecell(sheet,x,y,z); /*}}}*/ switch (del) { /* IN_X */ /*{{{*/ case IN_X: { for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) for (x=x1; x<=sheet->dimx-(x2-x1+1); ++x) { if (x+(x2-x1+1)dimx && ydimy && zdimz) { SHEET(sheet,x,y,z)=SHEET(sheet,x+(x2-x1+1),y,z); SHEET(sheet,x+(x2-x1+1),y,z)=(Cell*)0; } } break; } /*}}}*/ /* IN_Y */ /*{{{*/ case IN_Y: { for (z=z1; z<=z2; ++z) for (x=x1; x<=x2; ++x) for (y=y1; y<=sheet->dimy-(y2-y1+1); ++y) { if (xdimx && y+(y2-y1+1)dimy && zdimz) { SHEET(sheet,x,y,z)=SHEET(sheet,x,y+(y2-y1+1),z); SHEET(sheet,x,y+(y2-y1+1),z)=(Cell*)0; } } break; } /*}}}*/ /* IN_Z */ /*{{{*/ case IN_Z: { for (y=y1; y<=y2; ++y) for (x=x1; x<=x2; ++x) for (z=z1; z<=sheet->dimz-(z2-z1+1); ++z) { if (xdimx && ydimy && z+(z2-z1+1)dimz) { SHEET(sheet,x,y,z)=SHEET(sheet,x,y,z+(z2-z1+1)); SHEET(sheet,x,y,z+(z2-z1+1))=(Cell*)0; } } break; } /*}}}*/ /* default */ /*{{{*/ default: assert(0); /*}}}*/ } sheet->changed=1; cachelabels(sheet); forceupdate(sheet); } /*}}}*/ /* moveblock -- move a block */ /*{{{*/ void moveblock(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int copy) { /* variables */ /*{{{*/ int dirx, diry, dirz; int widx, widy, widz; int x, y, z; int xf, xt, yf, yt, zf, zt; /*}}}*/ if (x1==x3 && y1==y3 && z1==z3) return; widx=(x2-x1); widy=(y2-y1); widz=(z2-z1); if (x3>x1) { dirx=-1; xf=widx; xt=-1; } else { dirx=1; xf=0; xt=widx+1; } if (y3>y1) { diry=-1; yf=widy; yt=-1; } else { diry=1; yf=0; yt=widy+1; } if (z3>z1) { dirz=-1; zf=widz; zt=-1; } else { dirz=1; zf=0; zt=widz+1; } for (x=xf; x!=xt; x+=dirx) for (y=yf; y!=yt; y+=diry) for (z=zf; z!=zt; z+=dirz) { if (copy) { copycell(sheet,x1+x,y1+y,z1+z,sheet,x3+x,y3+y,z3+z); } else { if (x1+xdimx && y1+ydimy && z1+zdimz) { resize(sheet,x3+x,y3+y,z3+z); SHEET(sheet,x3+x,y3+y,z3+z)=SHEET(sheet,x1+x,y1+y,z1+z); SHEET(sheet,x1+x,y1+y,z1+z)=(Cell*)0; } else { freecell(sheet,x3+x,y3+y,z3+z); } } } sheet->changed=1; cachelabels(sheet); forceupdate(sheet); } /*}}}*/ /* sortblock -- sort a block */ /*{{{*/ /* Notes */ /*{{{*/ /* The idea is to sort a block of cells in one direction by swapping the planes which are canonical to the sort key vectors. An example is to sort a two dimensional block line-wise with one column as sort key. You can have multiple sort keys which all have the same direction and you can sort a cube plane-wise. */ /*}}}*/ const char *sortblock(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction dir, Sortkey *sk, size_t sklen) { /* variables */ /*{{{*/ int x,y,z; int incx=0,incy=0,incz=0; int distx,disty,distz; int i,r=-3,norel,work; /*}}}*/ /* asserts */ /*{{{*/ assert(sklen>0); assert(x1>=0); assert(x2>=0); assert(y1>=0); assert(y2>=0); assert(z1>=0); assert(z2>=0); /*}}}*/ norel=0; posorder(&x1,&x2); posorder(&y1,&y2); posorder(&z1,&z2); distx=(x2-x1+1); disty=(y2-y1+1); distz=(z2-z1+1); switch (dir) { case IN_X: incx=1; --x2; incy=0; incz=0; distx=1; break; case IN_Y: incx=0; incy=1; --y2; incz=0; disty=1; break; case IN_Z: incx=0; incy=0; incz=1; --z2; distz=1; break; default: assert(0); } assert(incx || incy || incz); do { work=0; for (x=x1,y=y1,z=z1; x<=x2&&y<=y2&&z<=z2; x+=incx,y+=incy,z+=incz) { for (i=0; ichanged=1; if (norel) return _("uncomparable elements"); else return (const char*)0; } /*}}}*/ /* mirrorblock -- mirror a block */ /*{{{*/ void mirrorblock(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction dir) { switch (dir) { case IN_X: /* left-right */ /*{{{*/ { int x,middle=(x2-x1+1)/2; for (x=0; xchanged=1; cachelabels(sheet); forceupdate(sheet); } /*}}}*/