/* #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 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; \ } /*}}}*/ /* 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; /*}}}*/ /* copytokens -- copy a sequence of tokens, possibly reallocating dest */ /*{{{*/ static void copytokens(Token*** totoks, Token** fromtoks) { /* variables */ /*{{{*/ size_t from_len, i; /*}}}*/ from_len = tveclen(fromtoks); if (from_len == 0) { tvecfree(*totoks); *totoks = EMPTY_TVEC; return; } from_len; if (from_len > tveclen(*totoks)) { tvecfree(*totoks); *totoks = malloc((from_len+1)*sizeof(Token*)); (*totoks)[from_len] = NULLTOKEN; } else { tvecfreetoks(*totoks); } for (i=0; ichanged=1; fromcell = CELL_AT(sheet1,x1,y1,z1); if (fromcell == NULLCELL) freecell(sheet2,x2,y2,z2); else /* copy first cell to second */ /*{{{*/ { freecell(sheet2,x2,y2,z2); initcell(sheet2,x2,y2,z2); tocell = CELL_AT(sheet2,x2,y2,z2); memcpy(tocell, fromcell, sizeof(Cell)); copytokens(&(tocell->contents), fromcell->contents); copytokens(&(tocell->ccontents), fromcell->ccontents); if (fromcell->label != (char*)0) tocell->label = strcpy(malloc(strlen(fromcell->label)+1), fromcell->label); tocell->value.type=EMPTY; } /*}}}*/ } else freecell(sheet2,x2,y2,z2); } /*}}}*/ /* dump_cell -- write internal contents of cell to standard out */ /*{{{*/ static void dump_cell(Sheet *sheet, int x, int y, int z) { Cell* c; char buf[2048]; assert(sheet != (Sheet*)0); if (x < 0 || y < 0 || z < 0) { printf("TEADUMP: Requested cell &(%d,%d,%d) has negative coordinates.\n", x, y, z); return; } if (!LOC_WITHIN(sheet, x, y, z)) { printf("TEADUMP: Requested cell &(%d,%d,%d) outside sheet dims %d,%d,%d.\n", x, y, z, sheet->dimx, sheet->dimy, sheet->dimz); return; } c = CELL_AT(sheet, x, y, z); if (c == NULLCELL) { printf("TEADUMP: Cell at &(%d,%d,%d) is empty.\n", x, y, z); return; } printf("TEADUMP of &(%d,%d,%d):\n", x, y, z); print(buf, sizeof(buf), 0, 1, 0, -1, c->contents); printf(" Base expr: %s.\n", buf); print(buf, sizeof(buf), 0, 1, 0, -1, c->ccontents); printf(" Update expr: %s.\n", buf); if (c->label == (char *)0) 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; case CENTER: printf("CENTER\n"); break; case AUTOADJUST: printf("AUTO\n"); break; } printf(" Precision: %d\n Attributes: ", c->precision); if (c->updated) printf("updated "); if (c->shadowed) printf("shadowed "); if (c->scientific) printf("scientific "); if (c->locked) printf("locked "); if (c->transparent) printf("transparent "); if (c->ignored) printf("ignored "); if (c->clock_t0) printf("clock_t0 "); if (c->clock_t1) printf("clock_t1 "); if (c->clock_t2) printf("clock_t2 "); if (c->bold) printf("bold "); if (c->underline) printf("underline "); printf("\n\n"); } /*}}}*/ /* dump_current_cell -- dump_cell of the current_cell */ /*{{{*/ void dump_current_cell(Sheet *sheet) { assert(sheet != (Sheet *)0); dump_cell(sheet, sheet->curx, sheet->cury, sheet->curz); } /*}}}*/ /* 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) { Cell *leftcell, *rightcell; assert(sheet1!=(Sheet*)0); assert(sheet2!=(Sheet*)0); /* empty cells are smaller than any non-empty cell */ /*{{{*/ if (!CELL_IS_GOOD(sheet1,x1,y1,z1) || CELL_AT(sheet1,x1,y1,z1)->value.type==EMPTY) { if (!CELL_IS_GOOD(sheet2,x2,y2,z2) || CELL_AT(sheet2,x2,y2,z2)->value.type==EMPTY) return 0; else return (sortkey&ASCENDING ? -1 : 1); } if (!CELL_IS_GOOD(sheet2,x2,y2,z2) || CELL_AT(sheet2,x2,y2,z2)->value.type==EMPTY) return (sortkey&ASCENDING ? 1 : -1); /*}}}*/ leftcell = CELL_AT(sheet1,x1,y1,z1); rightcell = CELL_AT(sheet2,x2,y2,z2); switch (leftcell->value.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; } /*}}}*/ /* FLOAT */ /*{{{*/ case FLOAT: { if (rightcell->value.type==FLOAT) { if (leftcell->value.u.fltvalue.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.fltvalue.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; } /*}}}*/ /* INT */ /*{{{*/ case INT: { if (rightcell->value.type==INT) { if (leftcell->value.u.integervalue.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.integervalue.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; } /*}}}*/ 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 (!LOC_WITHIN(sheet,x,y,z)) { /* 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; xsheet!=(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) { Cell *nc; assert(x>=0); assert(y>=0); assert(z>=0); resize(sheet,x,y,z); if (CELL_AT(sheet,x,y,z) == NULLCELL) { sheet->changed=1; nc = malloc(sizeof(Cell)); CELL_AT(sheet,x,y,z) = nc; nc->contents = EMPTY_TVEC; nc->ccontents = EMPTY_TVEC; nc->label=(char*)0; nc->adjust=AUTOADJUST; nc->precision=-1; nc->shadowed=0; nc->bold=0; nc->underline=0; nc->scientific=DEF_SCIENTIFIC; nc->value.type=EMPTY; nc->resvalue.type=EMPTY; nc->locked=0; nc->ignored=0; nc->clock_t0=0; nc->clock_t1=0; nc->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 (ALL_CELLS_IN_SHEET(sheet,x,y,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 (ALL_CELLS_IN_SHEET(sheet,x,y,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) != NULLCELL) { (*(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) { Cell *c; assert(sheet!=(Sheet*)0); if (sheet->sheet!=(Cell**)0 && CELL_IS_GOOD(sheet,x,y,z)) { c = CELL_AT(sheet,x,y,z); tvecfree(c->contents); tvecfree(c->ccontents); tfree(&(c->value)); tfree(&(c->resvalue)); free(c); CELL_AT(sheet,x,y,z) = NULLCELL; 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(CELL_AT(sheet,x,y,z)->ccontents); CELL_AT(sheet,x,y,z)->ccontents=t; } else { tvecfree(CELL_AT(sheet,x,y,z)->contents); CELL_AT(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 (!CELL_IS_GOOD(sheet,x,y,z)) return EMPTY_TVEC; else if (c==2) return (CELL_AT(sheet,x,y,z)->clock_t0 && CELL_AT(sheet,x,y,z)->ccontents ? CELL_AT(sheet,x,y,z)->ccontents : CELL_AT(sheet,x,y,z)->contents); else return (c ? CELL_AT(sheet,x,y,z)->ccontents : CELL_AT(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")); return result; } /*}}}*/ result.type = EMPTY; /* Can always short-circuit an out-of-bounds or empty cell */ if (!CELL_IS_GOOD(sheet,x,y,z)) return result; /* only short-circuit on empty contents of a good cell if we are NOT depending on this call to update the current value */ if (!upd_clock && getcont(sheet,x,y,z,2) == EMPTY_TVEC) return result; /* update value of this cell if needed and return it */ /*{{{*/ orig_upd_clock = upd_clock; if (CELL_AT(sheet,x,y,z)->ignored) { /* variables */ /*{{{*/ Token oldvalue; /*}}}*/ oldvalue=CELL_AT(sheet,x,y,z)->value; CELL_AT(sheet,x,y,z)->updated=1; CELL_AT(sheet,x,y,z)->value.type=EMPTY; tfree(&oldvalue); } else if (CELL_AT(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 (CELL_AT(sheet,x,y,z)->clock_t1==0) { CELL_AT(sheet,x,y,z)->updated = 1; oldvalue = CELL_AT(sheet,x,y,z)->value; upd_clock = 0; CELL_AT(sheet,x,y,z)->value = eval_safe(getcont(sheet, x, y, z, 2)); tfree(&oldvalue); } else if (upd_clock) { CELL_AT(sheet,x,y,z)->updated=1; upd_clock=0; oldvalue=CELL_AT(sheet,x,y,z)->resvalue; CELL_AT(sheet,x,y,z)->resvalue = eval_safe(getcont(sheet,x,y,z,2)); tfree(&oldvalue); } 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(CELL_AT(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 (ALL_CELLS_IN_SHEET(sheet,x,y,z)) { if (CELL_AT(sheet,x,y,z) && CELL_AT(sheet,x,y,z)->clock_t2) { CELL_AT(sheet,x,y,z)->updated=0; CELL_AT(sheet,x,y,z)->clock_t0=1; CELL_AT(sheet,x,y,z)->clock_t1=1; CELL_AT(sheet,x,y,z)->clock_t2=0; } } for (ALL_CELLS_IN_SHEET(sheet,x,y,z)) { upd_clock=1; getvalue(sheet,x,y,z); } for (ALL_CELLS_IN_SHEET(sheet,x,y,z)) { if (CELL_AT(sheet,x,y,z) && CELL_AT(sheet,x,y,z)->clock_t1) { tfree(&(CELL_AT(sheet,x,y,z)->value)); CELL_AT(sheet,x,y,z)->value = CELL_AT(sheet,x,y,z)->resvalue; CELL_AT(sheet,x,y,z)->resvalue.type = EMPTY; CELL_AT(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 t; assert(sheet!=(Sheet*)0); t=getvalue(sheet,x,y,z); printtok(s,size,chars,quote,scientific,precision,0,&t); tfree(&t); } /*}}}*/ /* getadjust -- get cell adjustment */ /*{{{*/ Adjust getadjust(Sheet *sheet, int x, int y, int z) { assert(sheet!=(Sheet*)0); if (!CELL_IS_GOOD(sheet,x,y,z)) { return LEFT; } else if (CELL_AT(sheet,x,y,z)->adjust==AUTOADJUST) return (CELL_AT(sheet,x,y,z)->value.type==INT || CELL_AT(sheet,x,y,z)->value.type==FLOAT ? RIGHT : LEFT); else return (CELL_AT(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); CELL_AT(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); CELL_AT(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); CELL_AT(sheet,x,y,z)->bold=yep; } /*}}}*/ /* isbold -- is cell bold? */ /*{{{*/ int isbold(Sheet *sheet, int x, int y, int z) { return (CELL_IS_GOOD(sheet,x,y,z) && CELL_AT(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); CELL_AT(sheet,x,y,z)->underline=yep; } /*}}}*/ /* isunderline -- is cell underlined? */ /*{{{*/ int underlined(Sheet *sheet, int x, int y, int z) { return (CELL_IS_GOOD(sheet,x,y,z) && CELL_AT(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); CELL_AT(sheet,x,y,z)->locked=yep; } /*}}}*/ /* locked -- is cell locked? */ /*{{{*/ int locked(Sheet *sheet, int x, int y, int z) { return (CELL_IS_GOOD(sheet,x,y,z) && CELL_AT(sheet,x,y,z)->locked); } /*}}}*/ /* transparent -- is cell transparent? */ /*{{{*/ int transparent(Sheet *sheet, int x, int y, int z) { return (CELL_IS_GOOD(sheet,x,y,z) && CELL_AT(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); CELL_AT(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); CELL_AT(sheet,x,y,z)->ignored=yep; } /*}}}*/ /* ignored -- is cell ignored? */ /*{{{*/ int ignored(Sheet *sheet, int x, int y, int z) { return (CELL_IS_GOOD(sheet,x,y,z) && CELL_AT(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 (CELL_AT(sheet,x,y,z)) { CELL_AT(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); CELL_AT(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 (CELL_IS_GOOD(sheet,x,y,z)) return CELL_AT(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); CELL_AT(sheet,x,y,z)->precision=precision; } /*}}}*/ /* getprecision -- get cell precision */ /*{{{*/ int getprecision(Sheet *sheet, int x, int y, int z) { if (CELL_IS_GOOD(sheet,x,y,z)) return (CELL_AT(sheet,x,y,z)->precision==-1 ? def_precision : CELL_AT(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 (!CELL_IS_GOOD(sheet,x,y,z) || CELL_AT(sheet,x,y,z)->label==(char*)0) return ""; else return (CELL_AT(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 (CELL_AT(sheet,x,y,z)->label!=(char*)0) free(CELL_AT(sheet,x,y,z)->label); if (*buf!='\0') CELL_AT(sheet,x,y,z)->label=strcpy(malloc(strlen(buf)+1),buf); else CELL_AT(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 (CELL_IS_GOOD(sheet,x,y,z)) { if (CELL_AT(sheet,x,y,z)->contents != EMPTY_TVEC) { for (run=CELL_AT(sheet,x,y,z)->contents; *run!=NULLTOKEN; ++run) { if ((*run)->type==LIDENT && strcmp((*run)->u.lident,oldlabel)==0) { free((*run)->u.lident); (*run)->u.lident=mystrmalloc(newlabel); } } } if (CELL_AT(sheet,x,y,z)->ccontents != EMPTY_TVEC) { for (run=CELL_AT(sheet,x,y,z)->ccontents; *run!=NULLTOKEN; ++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 (CELL_AT(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_AT(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 (CELL_AT(sheet,x,y,z)!=NULLCELL) { 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 (CELL_AT(sheet,x,y,z)!=NULLCELL) { 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 (CELL_AT(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 (CELL_AT(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 (CELL_AT(sheet,x,y,z)!=NULLCELL) { fprintf(fp,"C%d %d %d ",x,y,z); if (CELL_AT(sheet,x,y,z)->adjust!=AUTOADJUST) fprintf(fp,"A%c ","lrc"[CELL_AT(sheet,x,y,z)->adjust]); if (CELL_AT(sheet,x,y,z)->label) fprintf(fp,"L%s ",CELL_AT(sheet,x,y,z)->label); if (CELL_AT(sheet,x,y,z)->precision!=-1) fprintf(fp,"P%d ",CELL_AT(sheet,x,y,z)->precision); if (CELL_AT(sheet,x,y,z)->shadowed) fprintf(fp,"S "); if (CELL_AT(sheet,x,y,z)->bold) fprintf(fp,"B "); if (CELL_AT(sheet,x,y,z)->underline) fprintf(fp,"U "); if (CELL_AT(sheet,x,y,z)->scientific!=DEF_SCIENTIFIC) fprintf(fp,"E "); if (CELL_AT(sheet,x,y,z)->locked) fprintf(fp,"C "); if (CELL_AT(sheet,x,y,z)->transparent) fprintf(fp,"T "); if (CELL_AT(sheet,x,y,z)->contents) { char buf[4096]; if (fputc_close(':',fp)==EOF) return strerror(errno); print(buf,sizeof(buf),0,1,CELL_AT(sheet,x,y,z)->scientific,CELL_AT(sheet,x,y,z)->precision,CELL_AT(sheet,x,y,z)->contents); if (fputs_close(buf,fp)==EOF) return strerror(errno); } if (CELL_AT(sheet,x,y,z)->ccontents) { char buf[4096]; if (fputs_close("\\\n",fp)==EOF) return strerror(errno); print(buf,sizeof(buf),0,1,CELL_AT(sheet,x,y,z)->scientific,CELL_AT(sheet,x,y,z)->precision,CELL_AT(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); CELL_AT(sheet,x,y,z)->adjust=adjust; CELL_AT(sheet,x,y,z)->label=label; CELL_AT(sheet,x,y,z)->precision=precision; CELL_AT(sheet,x,y,z)->shadowed=shadowed; CELL_AT(sheet,x,y,z)->bold=bold; CELL_AT(sheet,x,y,z)->underline=underline; CELL_AT(sheet,x,y,z)->scientific=scientific; CELL_AT(sheet,x,y,z)->locked=locked; CELL_AT(sheet,x,y,z)->transparent=transparent; CELL_AT(sheet,x,y,z)->ignored=ignored; CELL_AT(sheet,x,y,z)->contents=contents; CELL_AT(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,CELL_AT(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); CELL_AT(sheet,x,y,z)=CELL_AT(sheet,x-(x2-x1+1),y,z); CELL_AT(sheet,x-(x2-x1+1),y,z)=NULLCELL; } 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); CELL_AT(sheet,x,y,z)=CELL_AT(sheet,x,y-(y2-y1+1),z); CELL_AT(sheet,x,y-(y2-y1+1),z)=NULLCELL; } 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); CELL_AT(sheet,x,y,z)=CELL_AT(sheet,x,y,z-(z2-z1+1)); CELL_AT(sheet,x,y,z-(z2-z1+1))=NULLCELL; } 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) { CELL_AT(sheet,x,y,z)=CELL_AT(sheet,x+(x2-x1+1),y,z); CELL_AT(sheet,x+(x2-x1+1),y,z)=NULLCELL; } } 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) { CELL_AT(sheet,x,y,z)=CELL_AT(sheet,x,y+(y2-y1+1),z); CELL_AT(sheet,x,y+(y2-y1+1),z)=NULLCELL; } } 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) { CELL_AT(sheet,x,y,z)=CELL_AT(sheet,x,y,z+(z2-z1+1)); CELL_AT(sheet,x,y,z+(z2-z1+1))=NULLCELL; } } 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); CELL_AT(sheet,x3+x,y3+y,z3+z)=CELL_AT(sheet,x1+x,y1+y,z1+z); CELL_AT(sheet,x1+x,y1+y,z1+z)=NULLCELL; } 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); } /*}}}*/