"Fossies" - the Fresh Open Source Software Archive

Member "teapot-2.3.0/sheet.c" (6 Feb 2012, 57040 Bytes) of package /linux/privat/old/teapot-2.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sheet.c" see the Fossies "Dox" file reference documentation.

    1 /* #includes */ /*{{{C}}}*//*{{{*/
    2 #ifdef DMALLOC
    3 #include "dmalloc.h"
    4 #endif
    5 
    6 #include <assert.h>
    7 #include <errno.h>
    8 #include <limits.h>
    9 #include <stdio.h>
   10 #include <stdlib.h>
   11 #include <string.h>
   12 
   13 
   14 #include "csv.h"
   15 #include "default.h"
   16 #include "display.h"
   17 #include "eval.h"
   18 #include "main.h"
   19 #include "misc.h"
   20 #include "parser.h"
   21 #include "scanner.h"
   22 #include "sheet.h"
   23 #include "utf8.h"
   24 #include "xdr.h"
   25 
   26 /*}}}*/
   27 /* #defines */ /*{{{*/
   28 #define SHEET(s,x,y,z) (*(s->sheet+(x)*s->dimz*s->dimy+(y)*s->dimz+(z)))
   29 
   30 #define HASH(x,s) \
   31 { \
   32   const unsigned char *S=(const unsigned char*)s; \
   33   \
   34   x=0; \
   35   while (*S) { x=(x<<5)+((x>>27)^*S); ++S; } \
   36   x%=LABEL_CACHE; \
   37 }
   38 
   39 #define SHADOWED(sheet,x,y,z) (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->shadowed)
   40 /*}}}*/
   41 
   42 /* variables */ /*{{{*/
   43 static int upd_clock; /* evaluate clocked expressions */
   44 /* Used during evaluation of a cell to specify the currently updated cell */
   45 Sheet *upd_sheet;
   46 int upd_x;
   47 int upd_y;
   48 int upd_z;
   49 int max_eval;
   50 /*}}}*/
   51 
   52 /* copycell      -- copy a cell */ /*{{{*/
   53 static void copycell(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2)
   54 {
   55   /* variables */ /*{{{*/
   56   Token **run;
   57   int i,len;
   58   /*}}}*/
   59 
   60   assert(sheet1!=(Sheet*)0);
   61   assert(sheet2!=(Sheet*)0);
   62   if (x1<sheet1->dimx && y1<sheet1->dimy && z1<sheet1->dimz)
   63   {
   64     sheet2->changed=1;
   65     if (SHEET(sheet1,x1,y1,z1)==(Cell*)0) freecell(sheet2,x2,y2,z2);
   66     else
   67     /* copy first cell to second */ /*{{{*/
   68     {
   69       freecell(sheet2,x2,y2,z2);
   70       initcell(sheet2,x2,y2,z2);
   71       memcpy(SHEET(sheet2,x2,y2,z2),SHEET(sheet1,x1,y1,z1),sizeof(Cell));
   72       if (SHEET(sheet1,x1,y1,z1)->contents!=(Token**)0)
   73       {
   74         for (len=1,run=SHEET(sheet1,x1,y1,z1)->contents; *run!=(Token*)0; ++len,++run);
   75         SHEET(sheet2,x2,y2,z2)->contents=malloc(len*sizeof(Token*));
   76         for (i=0; i<len; ++i)
   77         {
   78           if (*(SHEET(sheet1,x1,y1,z1)->contents+i)==(Token*)0) *(SHEET(sheet2,x2,y2,z2)->contents+i)=(Token*)0;
   79           else
   80           {
   81             *(SHEET(sheet2,x2,y2,z2)->contents+i)=malloc(sizeof(Token));
   82             **(SHEET(sheet2,x2,y2,z2)->contents+i)=tcopy(**(SHEET(sheet1,x1,y1,z1)->contents+i));
   83           }
   84         }
   85       }
   86       if (SHEET(sheet1,x1,y1,z1)->ccontents!=(Token**)0)
   87       {
   88         for (len=1,run=SHEET(sheet1,x1,y1,z1)->ccontents; *run!=(Token*)0; ++len,++run);
   89         SHEET(sheet2,x2,y2,z2)->ccontents=malloc(len*sizeof(Token*));
   90         for (i=0; i<len; ++i)
   91         {
   92           if (*(SHEET(sheet1,x1,y1,z1)->ccontents+i)==(Token*)0) *(SHEET(sheet2,x2,y2,z2)->ccontents+i)=(Token*)0;
   93           else
   94           {
   95             *(SHEET(sheet2,x2,y2,z2)->ccontents+i)=malloc(sizeof(Token));
   96             **(SHEET(sheet2,x2,y2,z2)->ccontents+i)=tcopy(**(SHEET(sheet1,x1,y1,z1)->ccontents+i));
   97           }
   98         }
   99       }
  100       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);
  101       SHEET(sheet2,x2,y2,z2)->value.type=EMPTY;
  102     }
  103     /*}}}*/
  104   }
  105   else freecell(sheet2,x2,y2,z2);
  106 }
  107 /*}}}*/
  108 /* swapblock     -- swap two non-overlapping blocks of cells */ /*{{{*/
  109 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)
  110 {
  111   int xoff, yoff, zoff;
  112 
  113   assert(sheet1!=(Sheet*)0);
  114   assert(sheet2!=(Sheet*)0);
  115   resize(sheet1,x1+xdist-1,y1+ydist-1,z1+zdist-1);
  116   resize(sheet2,x2+xdist-1,y2+ydist-1,z2+zdist-1);
  117   for (xoff=0; xoff<xdist; ++xoff)
  118   for (yoff=0; yoff<ydist; ++yoff)
  119   for (zoff=0; zoff<zdist; ++zoff)
  120   {
  121     Cell *t;
  122 
  123     t=SHEET(sheet1,x1+xoff,y1+yoff,z1+zoff);
  124     SHEET(sheet1,x1+xoff,y1+yoff,z1+zoff)=SHEET(sheet2,x2+xoff,y2+yoff,z2+zoff);
  125     SHEET(sheet2,x2+xoff,y2+yoff,z2+zoff)=t;
  126   }
  127   sheet1->changed=1;
  128   sheet2->changed=1;
  129 }
  130 /*}}}*/
  131 /* cmpcell       -- compare to cells with given order flags */ /*{{{*/
  132 /* Notes */ /*{{{*/
  133 /*
  134 Compare the _values_ of two cells.  The result is -1 if first is smaller
  135 than second, 0 if they are equal and 1 if the first is bigger than the
  136 second.  A result of 2 means they are not comparable.
  137 */
  138 /*}}}*/
  139 static int cmpcell(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2, int sortkey)
  140 {
  141   assert(sheet1!=(Sheet*)0);
  142   assert(sheet2!=(Sheet*)0);
  143   /* empty cells are smaller than any non-empty cell */ /*{{{*/
  144   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)
  145   {
  146     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;
  147     else return (sortkey&ASCENDING ? -1 : 1);
  148   }
  149   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);
  150   /*}}}*/
  151   switch (SHEET(sheet1,x1,y1,z1)->value.type)
  152   {
  153     /* STRING */ /*{{{*/
  154     case STRING:
  155     {
  156       if (SHEET(sheet2,x2,y2,z2)->value.type==STRING)
  157       {
  158         int r;
  159 
  160         r=strcmp(SHEET(sheet1,x1,y1,z1)->value.u.string,SHEET(sheet2,x2,y2,z2)->value.u.string);
  161         if (r<0) return (sortkey&ASCENDING ? -1 : 1);
  162         else if (r==0) return 0;
  163         else return (sortkey&ASCENDING ? 1 : -1);
  164       }
  165       return 2;
  166     }
  167     /*}}}*/
  168     /* FLOAT */ /*{{{*/
  169     case FLOAT:
  170     {
  171       if (SHEET(sheet2,x2,y2,z2)->value.type==FLOAT)
  172       {
  173         if (SHEET(sheet1,x1,y1,z1)->value.u.flt<SHEET(sheet2,x2,y2,z2)->value.u.flt) return (sortkey&ASCENDING ? -1 : 1);
  174         else if (SHEET(sheet1,x1,y1,z1)->value.u.flt==SHEET(sheet2,x2,y2,z2)->value.u.flt) return 0;
  175         else return (sortkey&ASCENDING ? 1 : -1);
  176       }
  177       if (SHEET(sheet2,x2,y2,z2)->value.type==INT)
  178       {
  179         if (SHEET(sheet1,x1,y1,z1)->value.u.flt<SHEET(sheet2,x2,y2,z2)->value.u.integer) return (sortkey&ASCENDING ? -1 : 1);
  180         else if (SHEET(sheet1,x1,y1,z1)->value.u.flt==SHEET(sheet2,x2,y2,z2)->value.u.integer) return 0;
  181         else return (sortkey&ASCENDING ? 1 : -1);
  182       }
  183       return 2;
  184     }
  185     /*}}}*/
  186     /* INT */ /*{{{*/
  187     case INT:
  188     {
  189       if (SHEET(sheet2,x2,y2,z2)->value.type==INT)
  190       {
  191         if (SHEET(sheet1,x1,y1,z1)->value.u.integer<SHEET(sheet2,x2,y2,z2)->value.u.integer) return (sortkey&ASCENDING ? -1 : 1);
  192         else if (SHEET(sheet1,x1,y1,z1)->value.u.integer==SHEET(sheet2,x2,y2,z2)->value.u.integer) return 0;
  193         else return (sortkey&ASCENDING ? 1 : -1);
  194       }
  195       if (SHEET(sheet2,x2,y2,z2)->value.type==FLOAT)
  196       {
  197         if (SHEET(sheet1,x1,y1,z1)->value.u.integer<SHEET(sheet2,x2,y2,z2)->value.u.flt) return (sortkey&ASCENDING ? -1 : 1);
  198         else if (SHEET(sheet1,x1,y1,z1)->value.u.integer==SHEET(sheet2,x2,y2,z2)->value.u.flt) return 0;
  199         else return (sortkey&ASCENDING ? 1 : -1);
  200       }
  201       return 2;
  202     }
  203     /*}}}*/
  204     default: return 2;
  205   }
  206 }
  207 /*}}}*/
  208 
  209 /* resize        -- check if sheet needs to be resized in any dimension */ /*{{{*/
  210 void resize(Sheet *sheet, int x, int y, int z)
  211 {
  212   assert(x>=0);
  213   assert(y>=0);
  214   assert(z>=0);
  215   assert(sheet!=(Sheet*)0);
  216   if (x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz)
  217   {
  218     /* variables */ /*{{{*/
  219     Cell **newsheet;
  220     int *newcolumn;
  221     unsigned int ndimx,ndimy,ndimz;
  222     /*}}}*/
  223 
  224     sheet->changed=1;
  225     ndimx=(x>=sheet->dimx ? x+1 : sheet->dimx);
  226     ndimy=(y>=sheet->dimy ? y+1 : sheet->dimy);
  227     ndimz=(z>=sheet->dimz ? z+1 : sheet->dimz);
  228     /* allocate new sheet */ /*{{{*/
  229     newsheet=malloc(ndimx*ndimy*ndimz*sizeof(Cell*));
  230     for (x=0; x<ndimx; ++x) for (y=0; y<ndimy; ++y) for (z=0; z<ndimz; ++z)
  231     {
  232       if (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz) *(newsheet+x*ndimz*ndimy+y*ndimz+z)=SHEET(sheet,x,y,z);
  233       else *(newsheet+x*ndimz*ndimy+y*ndimz+z)=(Cell*)0;
  234     }
  235     if (sheet->sheet!=(Cell**)0) free(sheet->sheet);
  236     sheet->sheet=newsheet;
  237     /*}}}*/
  238     /* allocate new columns */ /*{{{*/
  239     if (x>sheet->dimx || z>=sheet->dimz)
  240     {
  241       newcolumn=malloc(ndimx*ndimz*sizeof(int));
  242       for (x=0; x<ndimx; ++x) for (z=0; z<ndimz; ++z)
  243       {
  244         if (x<sheet->dimx && z<sheet->dimz) *(newcolumn+x*ndimz+z)=*(sheet->column+x*sheet->dimz+z);
  245         else *(newcolumn+x*ndimz+z)=DEF_COLUMNWIDTH;
  246       }
  247       if (sheet->column!=(int*)0) free(sheet->column);
  248       sheet->column=newcolumn;
  249     }
  250     /*}}}*/
  251     sheet->dimx=ndimx;
  252     sheet->dimy=ndimy;
  253     sheet->dimz=ndimz;
  254   }
  255 }
  256 /*}}}*/
  257 /* initcell      -- initialise new cell, if it does not exist yet */ /*{{{*/
  258 void initcell(Sheet *sheet, int x, int y, int z)
  259 {
  260   assert(x>=0);
  261   assert(y>=0);
  262   assert(z>=0);
  263   resize(sheet,x,y,z);
  264   if (SHEET(sheet,x,y,z)==(Cell*)0)
  265   {
  266     sheet->changed=1;
  267     SHEET(sheet,x,y,z)=malloc(sizeof(Cell));
  268     SHEET(sheet,x,y,z)->contents=(Token**)0;
  269     SHEET(sheet,x,y,z)->ccontents=(Token**)0;
  270     SHEET(sheet,x,y,z)->label=(char*)0;
  271     SHEET(sheet,x,y,z)->adjust=AUTOADJUST;
  272     SHEET(sheet,x,y,z)->precision=-1;
  273     SHEET(sheet,x,y,z)->shadowed=0;
  274     SHEET(sheet,x,y,z)->bold=0;
  275     SHEET(sheet,x,y,z)->underline=0;
  276     SHEET(sheet,x,y,z)->scientific=DEF_SCIENTIFIC;
  277     SHEET(sheet,x,y,z)->value.type=EMPTY;
  278     SHEET(sheet,x,y,z)->resvalue.type=EMPTY;
  279     SHEET(sheet,x,y,z)->locked=0;
  280     SHEET(sheet,x,y,z)->ignored=0;
  281     SHEET(sheet,x,y,z)->clock_t0=0;
  282     SHEET(sheet,x,y,z)->clock_t1=0;
  283     SHEET(sheet,x,y,z)->clock_t2=0;
  284   }
  285 }
  286 /*}}}*/
  287 /* cachelabels   -- create new label cache */ /*{{{*/
  288 void cachelabels(Sheet *sheet)
  289 {
  290   int i,x,y,z;
  291 
  292   if (sheet==(Sheet*)0) return;
  293   for (i=0; i<LABEL_CACHE; ++i) /* free bucket */ /*{{{*/
  294   {
  295     struct Label *run;
  296 
  297     for (run=sheet->labelcache[i]; run!=(struct Label*)0;)
  298     {
  299       struct Label *runnext;
  300 
  301       runnext=run->next;
  302       free(run);
  303       run=runnext;
  304     }
  305     sheet->labelcache[i]=(struct Label*)0;
  306   }
  307   /*}}}*/
  308   for (x=0; x<sheet->dimx; ++x) for (y=0; y<sheet->dimy; ++y) for (z=0; z<sheet->dimz; ++z)
  309   /* cache all labels */ /*{{{*/
  310   {
  311     const char *l;
  312 
  313     l=getlabel(sheet,x,y,z);
  314     if (*l)
  315     {
  316       unsigned long hx;
  317       struct Label **run;
  318 
  319       HASH(hx,l);
  320       for (run=&sheet->labelcache[(unsigned int)hx]; *run!=(struct Label*)0 && strcmp(l,(*run)->label); run=&((*run)->next));
  321       if (*run==(struct Label*)0)
  322       {
  323         *run=malloc(sizeof(struct Label));
  324         (*run)->next=(struct Label*)0;
  325         (*run)->label=l;
  326         (*run)->x=x;
  327         (*run)->y=y;
  328         (*run)->z=z;
  329       }
  330       /* else we have a duplicate label, which _can_ happen under */
  331       /* unfortunate conditions.  Don't tell anybody. */
  332     }
  333   }
  334   /*}}}*/
  335 }
  336 /*}}}*/
  337 /* freesheet     -- free all cells of an entire spread sheet */ /*{{{*/
  338 void freesheet(Sheet *sheet, int all)
  339 {
  340   /* variables */ /*{{{*/
  341   int x,y,z;
  342   /*}}}*/
  343 
  344   assert(sheet!=(Sheet*)0);
  345   sheet->changed=0;
  346   for (x=0; x<sheet->dimx; ++x) for (y=0; y<sheet->dimy; ++y) for (z=0; z<sheet->dimz; ++z)
  347   {
  348     freecell(sheet,x,y,z);
  349   }
  350   if (all)
  351   {
  352     int i;
  353 
  354     for (i=0; i<LABEL_CACHE; ++i) /* free all buckets */ /*{{{*/
  355     {
  356       struct Label *run;
  357 
  358       for (run=sheet->labelcache[i]; run!=(struct Label*)0;)
  359       {
  360         struct Label *runnext;
  361 
  362         runnext=run->next;
  363         free(run);
  364         run=runnext;
  365       }
  366     }
  367     /*}}}*/
  368     if (sheet->sheet) free(sheet->sheet);
  369     if (sheet->column) free(sheet->column);
  370     if (sheet->name) free(sheet->name);
  371   }
  372   else
  373   {
  374     for (x=0; x<sheet->dimx; ++x) for (z=0; z<sheet->dimz; ++z)
  375     {
  376       *(sheet->column+x*sheet->dimz+z)=DEF_COLUMNWIDTH;
  377     }
  378     cachelabels(sheet);
  379     forceupdate(sheet);
  380   }
  381 }
  382 /*}}}*/
  383 /* forceupdate   -- clear all clock and update flags */ /*{{{*/
  384 void forceupdate(Sheet *sheet)
  385 {
  386   int i;
  387 
  388   assert(sheet!=(Sheet*)0);
  389   for (i=0; i<sheet->dimx*sheet->dimy*sheet->dimz; ++i) if (*(sheet->sheet+i)!=(Cell*)0)
  390   {
  391     (*(sheet->sheet+i))->updated=0;
  392     (*(sheet->sheet+i))->clock_t0=0;
  393     (*(sheet->sheet+i))->clock_t1=0;
  394     (*(sheet->sheet+i))->clock_t2=0;
  395   }
  396   update(sheet);
  397 }
  398 /*}}}*/
  399 /* freecell      -- free one cell */ /*{{{*/
  400 void freecell(Sheet *sheet, int x, int y, int z)
  401 {
  402   assert(sheet!=(Sheet*)0);
  403   if (sheet->sheet!=(Cell**)0 && x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0)
  404   {
  405     tvecfree(SHEET(sheet,x,y,z)->contents);
  406     tvecfree(SHEET(sheet,x,y,z)->ccontents);
  407     tfree(&(SHEET(sheet,x,y,z)->value));
  408     free(SHEET(sheet,x,y,z));
  409     SHEET(sheet,x,y,z)=(Cell*)0;
  410     sheet->changed=1;
  411   }
  412 }
  413 /*}}}*/
  414 /* columnwidth   -- get width of column */ /*{{{*/
  415 int columnwidth(Sheet *sheet, int x, int z)
  416 {
  417   assert(sheet!=(Sheet*)0);
  418   if (x<sheet->dimx && z<sheet->dimz) return (*(sheet->column+x*sheet->dimz+z));
  419   else return DEF_COLUMNWIDTH;
  420 }
  421 /*}}}*/
  422 /* setwidth      -- set width of column */ /*{{{*/
  423 void setwidth(Sheet *sheet, int x, int z, int width)
  424 {
  425   assert(sheet!=(Sheet*)0);
  426   resize(sheet,x,1,z);
  427   sheet->changed=1;
  428   *(sheet->column+x*sheet->dimz+z)=width;
  429 }
  430 /*}}}*/
  431 /* cellwidth     -- get width of a cell */ /*{{{*/
  432 int cellwidth(Sheet *sheet, int x, int y, int z)
  433 {
  434   int width;
  435 
  436   if (SHADOWED(sheet,x,y,z)) return 0;
  437   width=columnwidth(sheet,x,z);
  438   for (++x; SHADOWED(sheet,x,y,z); width+=columnwidth(sheet,x,z),++x);
  439   return width;
  440 }
  441 /*}}}*/
  442 /* putcont       -- assign new contents */ /*{{{*/
  443 void putcont(Sheet *sheet, int x, int y, int z, Token **t, int c)
  444 {
  445   assert(sheet!=(Sheet*)0);
  446   sheet->changed=1;
  447   resize(sheet,x,y,z);
  448   initcell(sheet,x,y,z);
  449   if (c)
  450   {
  451     tvecfree(SHEET(sheet,x,y,z)->ccontents);
  452     SHEET(sheet,x,y,z)->ccontents=t;
  453   }
  454   else
  455   {
  456     tvecfree(SHEET(sheet,x,y,z)->contents);
  457     SHEET(sheet,x,y,z)->contents=t;
  458   }
  459   redraw_cell(sheet, x, y, z);
  460 }
  461 /*}}}*/
  462 /* getcont       -- get contents */ /*{{{*/
  463 Token **getcont(Sheet *sheet, int x, int y, int z, int c)
  464 {
  465   if (x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz) return (Token**)0;
  466   else if (SHEET(sheet,x,y,z)==(Cell*)0) return (Token**)0;
  467   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);
  468   else return (c ? SHEET(sheet,x,y,z)->ccontents : SHEET(sheet,x,y,z)->contents);
  469 }
  470 /*}}}*/
  471 /* getvalue      -- get tcopy()ed value */ /*{{{*/
  472 Token getvalue(Sheet *sheet, int x, int y, int z)
  473 {
  474   /* variables */ /*{{{*/
  475   Token result;
  476   int orig_upd_clock;
  477   /*}}}*/
  478 
  479   assert(sheet!=(Sheet*)0);
  480   if (x<0 || y<0 || z<0)
  481   /* return error */ /*{{{*/
  482   {
  483     result.type=EEK;
  484     result.u.err=mystrmalloc(_("Negative index"));
  485   }
  486   /*}}}*/
  487   else if (getcont(sheet,x,y,z,2)==(Token**)0)
  488   /* return empty value */ /*{{{*/
  489   result.type=EMPTY;
  490   /*}}}*/
  491   else
  492   /* update value of this cell if needed and return it */ /*{{{*/
  493   {
  494     orig_upd_clock=upd_clock;
  495     if (SHEET(sheet,x,y,z)->ignored)
  496     {
  497       /* variables */ /*{{{*/
  498       Token oldvalue;
  499       /*}}}*/
  500 
  501       oldvalue=SHEET(sheet,x,y,z)->value;
  502       SHEET(sheet,x,y,z)->updated=1;
  503       SHEET(sheet,x,y,z)->value.type=EMPTY;
  504       tfree(&oldvalue);
  505     }
  506     else if (SHEET(sheet,x,y,z)->updated==0)
  507     {
  508       /* variables */ /*{{{*/
  509       Sheet *old_sheet;
  510       int old_x,old_y,old_z,old_max_eval;
  511       Token oldvalue;
  512       /*}}}*/
  513 
  514       old_sheet=upd_sheet;
  515       old_x=upd_x;
  516       old_y=upd_y;
  517       old_z=upd_z;
  518       old_max_eval=max_eval;
  519       upd_sheet=sheet;
  520       upd_x=x;
  521       upd_y=y;
  522       upd_z=z;
  523       max_eval=MAX_EVALNEST;
  524       if (SHEET(sheet,x,y,z)->clock_t1==0)
  525       {
  526         SHEET(sheet,x,y,z)->updated=1;
  527         oldvalue=SHEET(sheet,x,y,z)->value;
  528         upd_clock=0;
  529         SHEET(sheet,x,y,z)->value=eval(getcont(sheet,x,y,z,2));
  530         tfree(&oldvalue);
  531       }
  532       else if (upd_clock)
  533       {
  534         SHEET(sheet,x,y,z)->updated=1;
  535         upd_clock=0;
  536         SHEET(sheet,x,y,z)->resvalue=eval(getcont(sheet,x,y,z,2));
  537       }
  538       upd_sheet=old_sheet;
  539       upd_x=old_x;
  540       upd_y=old_y;
  541       upd_z=old_z;
  542       max_eval=old_max_eval;
  543     }
  544     if (!orig_upd_clock) result=tcopy(SHEET(sheet,x,y,z)->value);
  545   }
  546   /*}}}*/
  547   return result;
  548 }
  549 /*}}}*/
  550 /* update        -- update all cells that need it */ /*{{{*/
  551 void update(Sheet *sheet)
  552 {
  553   int x,y,z,kp,iterating;
  554 
  555   assert(sheet!=(Sheet*)0);
  556   kp=0;
  557   iterating=0;
  558   do
  559   {
  560     sheet->clk=0;
  561     if (iterating==1)
  562     {
  563       line_msg((const char*)0,_("Calculating running, press Escape to abort it"));
  564       ++iterating;
  565     }
  566     else if (iterating==0) ++iterating;
  567     for (x=0; x<sheet->dimx; ++x) for (y=0; y<sheet->dimy; ++y) for (z=0; z<sheet->dimz; ++z)
  568     {
  569       if (SHEET(sheet,x,y,z) && SHEET(sheet,x,y,z)->clock_t2)
  570       {
  571         SHEET(sheet,x,y,z)->updated=0;
  572         SHEET(sheet,x,y,z)->clock_t0=1;
  573         SHEET(sheet,x,y,z)->clock_t1=1;
  574         SHEET(sheet,x,y,z)->clock_t2=0;
  575       }
  576     }
  577     for (x=0; x<sheet->dimx; ++x) for (y=0; y<sheet->dimy; ++y) for (z=0; z<sheet->dimz; ++z)
  578     {
  579       upd_clock=1;
  580       getvalue(sheet,x,y,z);
  581     }
  582     for (x=0; x<sheet->dimx; ++x) for (y=0; y<sheet->dimy; ++y) for (z=0; z<sheet->dimz; ++z)
  583     {
  584       if (SHEET(sheet,x,y,z) && SHEET(sheet,x,y,z)->clock_t1)
  585       {
  586         tfree(&(SHEET(sheet,x,y,z)->value));
  587         SHEET(sheet,x,y,z)->value=SHEET(sheet,x,y,z)->resvalue;
  588         SHEET(sheet,x,y,z)->clock_t1=0;
  589       }
  590     }
  591     upd_clock=0;
  592   } while (sheet->clk && !(kp=keypressed()));
  593   if (iterating==2) line_msg((const char*)0,kp ? _("Calculation aborted") : _("Calculation finished"));
  594   sheet->clk=0;
  595   redraw_sheet(sheet);
  596 }
  597 /*}}}*/
  598 /* geterror      -- get malloc()ed error string */ /*{{{*/
  599 char *geterror(Sheet *sheet, int x, int y, int z)
  600 {
  601   Token v;
  602 
  603   assert(sheet!=(Sheet*)0);
  604   if ((v=getvalue(sheet,x,y,z)).type!=EEK)
  605   {
  606     tfree(&v);
  607     return (char*)0;
  608   }
  609   else
  610   {
  611     return (v.u.err);
  612   }
  613 }
  614 /*}}}*/
  615 /* printvalue    -- get ASCII representation of value */ /*{{{*/
  616 void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, int x, int y, int z)
  617 {
  618   Token *tv[2],t;
  619 
  620   assert(sheet!=(Sheet*)0);
  621   t=getvalue(sheet,x,y,z); tv[0]=&t;
  622   tv[1]=(Token*)0;
  623   print(s,size,chars,quote,scientific,precision,tv);
  624   tfree(&t);
  625 }
  626 /*}}}*/
  627 /* getadjust     -- get cell adjustment */ /*{{{*/
  628 Adjust getadjust(Sheet *sheet, int x, int y, int z)
  629 {
  630   assert(sheet!=(Sheet*)0);
  631   if (x>=sheet->dimx || y>=sheet->dimy || z>=sheet->dimz || SHEET(sheet,x,y,z)==(Cell*)0)
  632   {
  633     return LEFT;
  634   }
  635   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);
  636   else return (SHEET(sheet,x,y,z)->adjust);
  637 }
  638 /*}}}*/
  639 /* setadjust     -- set cell adjustment */ /*{{{*/
  640 void setadjust(Sheet *sheet, int x, int y, int z, Adjust adjust)
  641 {
  642   assert(sheet!=(Sheet*)0);
  643   sheet->changed=1;
  644   resize(sheet,x,y,z);
  645   initcell(sheet,x,y,z);
  646   SHEET(sheet,x,y,z)->adjust=adjust;
  647 }
  648 /*}}}*/
  649 
  650 /* shadow        -- shadow cell by left neighbour */ /*{{{*/
  651 void shadow(Sheet *sheet, int x, int y, int z, int yep)
  652 {
  653   sheet->changed=1;
  654   initcell(sheet,x,y,z);
  655   SHEET(sheet,x,y,z)->shadowed=yep;
  656 }
  657 /*}}}*/
  658 /* shadowed      -- is cell shadowed? */ /*{{{*/
  659 int shadowed(Sheet *sheet, int x, int y, int z)
  660 {
  661   return (SHADOWED(sheet,x,y,z));
  662 }
  663 /*}}}*/
  664 /* bold        -- bold font */ /*{{{*/
  665 void bold(Sheet *sheet, int x, int y, int z, int yep)
  666 {
  667   sheet->changed=1;
  668   initcell(sheet,x,y,z);
  669   SHEET(sheet,x,y,z)->bold=yep;
  670 }
  671 /*}}}*/
  672 /* isbold      -- is cell bold? */ /*{{{*/
  673 int isbold(Sheet *sheet, int x, int y, int z)
  674 {
  675   return (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->bold);
  676 }
  677 /*}}}*/
  678 /* underline        -- underline */ /*{{{*/
  679 void underline(Sheet *sheet, int x, int y, int z, int yep)
  680 {
  681   sheet->changed=1;
  682   initcell(sheet,x,y,z);
  683   SHEET(sheet,x,y,z)->underline=yep;
  684 }
  685 /*}}}*/
  686 /* isunderline      -- is cell underlined? */ /*{{{*/
  687 int underlined(Sheet *sheet, int x, int y, int z)
  688 {
  689   return (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->underline);
  690 }
  691 /*}}}*/
  692 /* lockcell      -- lock cell */ /*{{{*/
  693 void lockcell(Sheet *sheet, int x, int y, int z, int yep)
  694 {
  695   sheet->changed=1;
  696   initcell(sheet,x,y,z);
  697   SHEET(sheet,x,y,z)->locked=yep;
  698 }
  699 /*}}}*/
  700 /* locked        -- is cell locked? */ /*{{{*/
  701 int locked(Sheet *sheet, int x, int y, int z)
  702 {
  703   return (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->locked);
  704 }
  705 /*}}}*/
  706 /* transparent   -- is cell transparent? */ /*{{{*/
  707 int transparent(Sheet *sheet, int x, int y, int z)
  708 {
  709   return (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->transparent);
  710 }
  711 /*}}}*/
  712 /* maketrans     -- make cell transparent */ /*{{{*/
  713 void maketrans(Sheet *sheet, int x, int y, int z, int yep)
  714 {
  715   sheet->changed=1;
  716   initcell(sheet,x,y,z);
  717   SHEET(sheet,x,y,z)->transparent=yep;
  718 }
  719 /*}}}*/
  720 /* igncell       -- ignore cell */ /*{{{*/
  721 void igncell(Sheet *sheet, int x, int y, int z, int yep)
  722 {
  723   sheet->changed=1;
  724   initcell(sheet,x,y,z);
  725   SHEET(sheet,x,y,z)->ignored=yep;
  726 }
  727 /*}}}*/
  728 /* ignored       -- is cell ignored? */ /*{{{*/
  729 int ignored(Sheet *sheet, int x, int y, int z)
  730 {
  731   return (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0 && SHEET(sheet,x,y,z)->ignored);
  732 }
  733 /*}}}*/
  734 /* clk           -- clock cell */ /*{{{*/
  735 void clk(Sheet *sheet, int x, int y, int z)
  736 {
  737   assert(sheet!=(Sheet*)0);
  738   assert(x>=0 && x<sheet->dimx);
  739   assert(y>=0 && y<sheet->dimy);
  740   assert(z>=0 && z<sheet->dimz);
  741   if (SHEET(sheet,x,y,z))
  742   {
  743     SHEET(sheet,x,y,z)->clock_t2=1;
  744     sheet->clk=1;
  745   }
  746 }
  747 /*}}}*/
  748 /* setscientific -- cell value should be displayed in scientific notation */ /*{{{*/
  749 void setscientific(Sheet *sheet, int x, int y, int z, int yep)
  750 {
  751   sheet->changed=1;
  752   resize(sheet,x,y,z);
  753   initcell(sheet,x,y,z);
  754   SHEET(sheet,x,y,z)->scientific=yep;
  755 }
  756 /*}}}*/
  757 /* getscientific -- should value be displayed in scientific notation? */ /*{{{*/
  758 int getscientific(Sheet *sheet, int x, int y, int z)
  759 {
  760   if (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0) return SHEET(sheet,x,y,z)->scientific;
  761   else return DEF_SCIENTIFIC;
  762 }
  763 /*}}}*/
  764 /* setprecision  -- set cell precision */ /*{{{*/
  765 void setprecision(Sheet *sheet, int x, int y, int z, int precision)
  766 {
  767   sheet->changed=1;
  768   resize(sheet,x,y,z);
  769   initcell(sheet,x,y,z);
  770   SHEET(sheet,x,y,z)->precision=precision;
  771 }
  772 /*}}}*/
  773 /* getprecision  -- get cell precision */ /*{{{*/
  774 int getprecision(Sheet *sheet, int x, int y, int z)
  775 {
  776   if (x<sheet->dimx && y<sheet->dimy && z<sheet->dimz && SHEET(sheet,x,y,z)!=(Cell*)0) return (SHEET(sheet,x,y,z)->precision==-1 ? def_precision : SHEET(sheet,x,y,z)->precision);
  777   else return def_precision;
  778 }
  779 /*}}}*/
  780 /* getlabel      -- get cell label */ /*{{{*/
  781 const char *getlabel(Sheet *sheet, int x, int y, int z)
  782 {
  783   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 "";
  784   else return (SHEET(sheet,x,y,z)->label);
  785 }
  786 /*}}}*/
  787 /* setlabel      -- set cell label */ /*{{{*/
  788 void setlabel(Sheet *sheet, int x, int y, int z, const char *buf, int update)
  789 {
  790   sheet->changed=1;
  791   resize(sheet,x,y,z);
  792   initcell(sheet,x,y,z);
  793   if (SHEET(sheet,x,y,z)->label!=(char*)0) free(SHEET(sheet,x,y,z)->label);
  794   if (*buf!='\0') SHEET(sheet,x,y,z)->label=strcpy(malloc(strlen(buf)+1),buf);
  795   else SHEET(sheet,x,y,z)->label=(char*)0;
  796   if (update)
  797   {
  798     cachelabels(sheet);
  799     forceupdate(sheet);
  800   }
  801 }
  802 /*}}}*/
  803 /* findlabel     -- return cell location for a given label */ /*{{{*/
  804 Token findlabel(Sheet *sheet, const char *label)
  805 {
  806   /* variables */ /*{{{*/
  807   Token result;
  808   unsigned long hx;
  809   struct Label *run;
  810   /*}}}*/
  811 
  812   assert(sheet!=(Sheet*)0);
  813 /*
  814   if (sheet==(Sheet*)0) run=(struct Label*)0;
  815   else
  816 */
  817   {
  818     HASH(hx,label);
  819     for (run=sheet->labelcache[(unsigned int)hx]; run!=(struct Label*)0 && strcmp(label,run->label); run=run->next);
  820   }
  821   if (run)
  822   {
  823     result.type=LOCATION;
  824     result.u.location[0]=run->x;
  825     result.u.location[1]=run->y;
  826     result.u.location[2]=run->z;
  827   }
  828   else
  829   {
  830     result.type=EEK;
  831     result.u.err=mystrmalloc(_("No such label"));
  832   }
  833   return result;
  834 }
  835 /*}}}*/
  836 /* relabel       -- search and replace for labels */ /*{{{*/
  837 void relabel(Sheet *sheet, const char *oldlabel, const char *newlabel, int x, int y, int z)
  838 {
  839   /* variables */ /*{{{*/
  840   Token **run;
  841   /*}}}*/
  842 
  843   /* asserts */ /*{{{*/
  844   assert(sheet!=(Sheet*)0);
  845   assert(oldlabel!=(const char*)0);
  846   assert(newlabel!=(const char*)0);
  847   assert(x>=0);
  848   assert(y>=0);
  849   assert(z>=0);
  850   /*}}}*/
  851   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))
  852   {
  853     for (run=SHEET(sheet,x,y,z)->contents; *run!=(Token*)0; ++run)
  854     {
  855       if ((*run)->type==LIDENT && strcmp((*run)->u.lident,oldlabel)==0)
  856       {
  857         free((*run)->u.lident);
  858         (*run)->u.lident=mystrmalloc(newlabel);
  859       }
  860     }
  861   }
  862   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))
  863   {
  864     for (run=SHEET(sheet,x,y,z)->ccontents; *run!=(Token*)0; ++run)
  865     {
  866       if ((*run)->type==LIDENT && strcmp((*run)->u.lident,oldlabel)==0)
  867       {
  868         free((*run)->u.lident);
  869         (*run)->u.lident=mystrmalloc(newlabel);
  870       }
  871     }
  872   }
  873   cachelabels(sheet);
  874   forceupdate(sheet);
  875 }
  876 /*}}}*/
  877 /* savexdr       -- save a spread sheet in XDR */ /*{{{*/
  878 const char *savexdr(Sheet *sheet, const char *name, unsigned int *count)
  879 {
  880   /* variables */ /*{{{*/
  881   FILE *fp;
  882   XDR xdrs;
  883   int x,y,z;
  884   /*}}}*/
  885 
  886   *count=0;
  887   if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
  888   xdrstdio_create(&xdrs,fp,XDR_ENCODE);
  889   if (!xdr_magic(&xdrs))
  890   {
  891     xdr_destroy(&xdrs);
  892     (void)fclose(fp);
  893     return strerror(errno);
  894   }
  895   for (x=sheet->dimx-1; x>=0; --x) for (z=sheet->dimz-1; z>=0; --z)
  896   {
  897     int width;
  898     int u;
  899 
  900     width=columnwidth(sheet,x,z);
  901     if (width!=DEF_COLUMNWIDTH)
  902     {
  903       u=0;
  904       if (xdr_int(&xdrs,&u)==0 || xdr_column(&xdrs,&x,&z,&width)==0)
  905       {
  906         xdr_destroy(&xdrs);
  907         (void)fclose(fp);
  908         return strerror(errno);
  909       }
  910     }
  911     for (y=sheet->dimy-1; y>=0; --y)
  912     {
  913       if (SHEET(sheet,x,y,z)!=(Cell*)0)
  914       {
  915         u=1;
  916         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)
  917         {
  918           xdr_destroy(&xdrs);
  919           (void)fclose(fp);
  920           return strerror(errno);
  921         }
  922         ++*count;
  923       }
  924     }
  925   }
  926   xdr_destroy(&xdrs);
  927   if (fclose(fp)==EOF) return strerror(errno);
  928   sheet->changed=0;
  929   return (const char*)0;
  930 }
  931 /*}}}*/
  932 /* savetbl       -- save as tbl tyble */ /*{{{*/
  933 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)
  934 {
  935   /* variables */ /*{{{*/
  936   FILE *fp=(FILE*)0; /* cause run time error */
  937   int x,y,z;
  938   char buf[1024];
  939   char num[20];
  940   char fullname[PATH_MAX];
  941   /*}}}*/
  942 
  943   /* asserts */ /*{{{*/
  944   assert(sheet!=(Sheet*)0);
  945   assert(name!=(const char*)0);
  946   /*}}}*/
  947   *count=0;
  948   for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) if (shadowed(sheet,x1,y,z)) return _("Shadowed cells in first column");
  949   if (!body && (fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
  950   for (z=z1; z<=z2; ++z)
  951   {
  952     if (body)
  953     /* open new file */ /*{{{*/
  954     {
  955       sprintf(num,".%d",z);
  956       fullname[sizeof(fullname)-strlen(num)-1]='\0';
  957       (void)strncpy(fullname,name,sizeof(fullname)-strlen(num)-1);
  958       fullname[sizeof(fullname)-1]='\0';
  959       (void)strncat(fullname,num,sizeof(fullname)-strlen(num)-1);
  960       fullname[sizeof(fullname)-1]='\0';
  961       if ((fp=fopen(fullname,"w"))==(FILE*)0) return strerror(errno);
  962     }
  963     /*}}}*/
  964     else if (fputs_close(".TS\n",fp)==EOF) return strerror(errno);
  965     for (y=y1; y<=y2; ++y)
  966     {
  967       /* print format */ /*{{{*/
  968       if (y>y1 && fputs_close(".T&\n",fp)==EOF) return strerror(errno);
  969       for (x=x1; x<=x2; ++x)
  970       {
  971         if (x>x1 && fputc_close(' ',fp)==EOF) return strerror(errno);
  972         if (shadowed(sheet,x,y,z))
  973         {
  974           if (fputc_close('s',fp)==EOF) return strerror(errno);
  975         }
  976         if (isbold(sheet,x,y,z))
  977         {
  978           if (fputc_close('b',fp)==EOF) return strerror(errno);
  979         }
  980         if (underlined(sheet,x,y,z))
  981         {
  982           if (fputc_close('u',fp)==EOF) return strerror(errno);
  983         }
  984         else switch (getadjust(sheet,x,y,z))
  985         {
  986           case LEFT: if (fputc_close('l',fp)==EOF) return strerror(errno); break;
  987           case RIGHT: if (fputc_close('r',fp)==EOF) return strerror(errno); break;
  988           case CENTER: if (fputc_close('c',fp)==EOF) return strerror(errno); break;
  989           default: assert(0);
  990         }
  991       }
  992       if (fputs_close(".\n",fp)==EOF) return strerror(errno);
  993       /*}}}*/
  994       /* print contents */ /*{{{*/
  995       for (x=x1; x<=x2; ++x)
  996       {
  997         if (!shadowed(sheet,x,y,z))
  998         {
  999           if (x>x1 && fputc_close('\t',fp)==EOF) return strerror(errno);
 1000           if (SHEET(sheet,x,y,z)!=(Cell*)0)
 1001           {
 1002             char *bufp;
 1003 
 1004             printvalue(buf,sizeof(buf),0,0,getscientific(sheet,x,y,z),getprecision(sheet,x,y,z),sheet,x,y,z);
 1005             if (transparent(sheet,x,y,z))
 1006             {
 1007               if (fputs_close(buf,fp)==EOF) return strerror(errno);
 1008             }
 1009             else for (bufp=buf; *bufp; ++bufp) switch (*bufp)
 1010             {
 1011               case '\\':
 1012               {
 1013                 if (fputc_close('\\',fp)==EOF || fputc_close('e',fp)==EOF) return strerror(errno);
 1014                 break;
 1015               }
 1016               case '_':
 1017               {
 1018                 if (fputc_close('\\',fp)==EOF || fputc_close('&',fp)==EOF || fputc_close('_',fp)==EOF) return strerror(errno);
 1019                 break;
 1020               }
 1021               case '.':
 1022               {
 1023                 if (x==x1 && bufp==buf && (fputc_close('\\',fp)==EOF || fputc_close('&',fp)==EOF)) return strerror(errno);
 1024                 if (fputc_close('.',fp)==EOF) return strerror(errno);
 1025                 break;
 1026               }
 1027               case '\'':
 1028               {
 1029                 if (x==x1 && bufp==buf && (fputc_close('\\',fp)==EOF || fputc_close('&',fp)==EOF)) return strerror(errno);
 1030                 if (fputc_close('\'',fp)==EOF) return strerror(errno);
 1031                 break;
 1032               }
 1033               case '-':
 1034               {
 1035                 if (*(bufp+1)=='-')
 1036                 {
 1037                   if (fputc_close('-',fp)==EOF) return strerror(errno);
 1038                   else ++bufp;
 1039                 }
 1040                 else if (fputs_close("\\-",fp)==EOF) return strerror(errno);
 1041                 break;
 1042               }
 1043               default: if (fputc_close(*bufp,fp)==EOF) return strerror(errno);
 1044             }
 1045           }
 1046         }
 1047       }
 1048       if (fputc_close('\n',fp)==EOF) return strerror(errno);
 1049       /*}}}*/
 1050       ++*count;
 1051     }
 1052     if (!body)
 1053     {
 1054       if (fputs_close(".TE\n",fp)==EOF) return strerror(errno);
 1055       if (z<z2 && fputs_close(".bp\n",fp)==EOF) return strerror(errno);
 1056     }
 1057     if (body && fclose(fp)==EOF) return strerror(errno);
 1058   }
 1059   if (!body && fclose(fp)==EOF) return strerror(errno);
 1060   return (const char*)0;
 1061 }
 1062 /*}}}*/
 1063 /* savetext      -- save as text */ /*{{{*/
 1064 const char *savetext(Sheet *sheet, const char *name, int x1, int y1, int z1, int x2, int y2, int z2, unsigned int *count)
 1065 {
 1066   /* variables */ /*{{{*/
 1067   FILE *fp;
 1068   int x,y,z;
 1069   /*}}}*/
 1070 
 1071   /* asserts */ /*{{{*/
 1072   assert(sheet!=(Sheet*)0);
 1073   assert(name!=(const char*)0);
 1074   /*}}}*/
 1075   *count=0;
 1076   for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) if (shadowed(sheet,x1,y,z)) return _("Shadowed cells in first column");
 1077   if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
 1078   for (z=z1; z<=z2; ++z)
 1079   {
 1080     for (y=y1; y<=y2; ++y)
 1081     {
 1082       size_t size,fill;
 1083 
 1084       for (x=x1; x<=x2; ++x)
 1085       {
 1086         size=cellwidth(sheet,x,y,z);
 1087         if (SHEET(sheet,x,y,z)!=(Cell*)0)
 1088         {
 1089           char *buf;
 1090 
 1091           buf=malloc(size*UTF8SZ+1);
 1092           printvalue(buf,size*UTF8SZ+1,size,0,getscientific(sheet,x,y,z),getprecision(sheet,x,y,z),sheet,x,y,z);
 1093           adjust(getadjust(sheet,x,y,z),buf,size);
 1094           if (fputs_close(buf,fp)==EOF)
 1095           {
 1096             free(buf);
 1097             return strerror(errno);
 1098           }
 1099           for (fill=strlen(buf); fill<size; ++fill) if (fputc_close(' ',fp)==EOF)
 1100           {
 1101             free(buf);
 1102             return strerror(errno);
 1103           }
 1104           free(buf);
 1105         }
 1106         else
 1107         {
 1108           for (fill=0; fill<size; ++fill) if (fputc_close(' ',fp)==EOF) return strerror(errno);
 1109         }
 1110         ++*count;
 1111       }
 1112       if (fputc_close('\n',fp)==EOF) return strerror(errno);
 1113     }
 1114     if (z<z2 && fputs_close("\f",fp)==EOF) return strerror(errno);
 1115   }
 1116   if (fclose(fp)==EOF) return strerror(errno);
 1117   return (const char*)0;
 1118 }
 1119 /*}}}*/
 1120 /* savecsv       -- save as CSV */ /*{{{*/
 1121 const char *savecsv(Sheet *sheet, const char *name, char sep, int x1, int y1, int z1, int x2, int y2, int z2, unsigned int *count)
 1122 {
 1123   /* variables */ /*{{{*/
 1124   FILE *fp;
 1125   int x,y,z;
 1126   /*}}}*/
 1127 
 1128   /* asserts */ /*{{{*/
 1129   assert(sheet!=(Sheet*)0);
 1130   assert(name!=(const char*)0);
 1131   /*}}}*/
 1132   *count=0;
 1133   for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) if (shadowed(sheet,x1,y,z)) return _("Shadowed cells in first column");
 1134   if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
 1135   for (z=z1; z<=z2; ++z)
 1136   {
 1137     for (y=y1; y<=y2; ++y)
 1138     {
 1139       for (x=x1; x<=x2; ++x)
 1140       {
 1141         if (x>x1) if (fputc_close(sep,fp)==EOF) return strerror(errno);
 1142         if (SHEET(sheet,x,y,z)!=(Cell*)0)
 1143         {
 1144           char *buf,*s;
 1145 
 1146           buf=malloc(255*UTF8SZ+1);
 1147           printvalue(buf,255*UTF8SZ+1,255,0,getscientific(sheet,x,y,z),getprecision(sheet,x,y,z),sheet,x,y,z);
 1148           if (SHEET(sheet,x,y,z)->value.type==STRING && fputc_close('"',fp)==EOF)
 1149           {
 1150             free(buf);
 1151             return strerror(errno);
 1152           }
 1153           for (s=buf; *s; ++s)
 1154           {
 1155             if (fputc_close(*s,fp)==EOF || (*s=='"' && fputc_close(*s,fp)==EOF))
 1156             {
 1157               free(buf);
 1158               return strerror(errno);
 1159             }
 1160           }
 1161           free(buf);
 1162           if (SHEET(sheet,x,y,z)->value.type==STRING && fputc_close('"',fp)==EOF) return strerror(errno);
 1163         }
 1164         ++*count;
 1165       }
 1166       if (fputc_close('\n',fp)==EOF) return strerror(errno);
 1167     }
 1168     if (z<z2 && fputs_close("\f",fp)==EOF) return strerror(errno);
 1169   }
 1170   if (fclose(fp)==EOF) return strerror(errno);
 1171   return (const char*)0;
 1172 }
 1173 /*}}}*/
 1174 /* saveport      -- save as portable text */ /*{{{*/
 1175 const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
 1176 {
 1177   /* variables */ /*{{{*/
 1178   FILE *fp;
 1179   int x,y,z;
 1180   /*}}}*/
 1181 
 1182   /* asserts */ /*{{{*/
 1183   assert(sheet!=(Sheet*)0);
 1184   assert(name!=(const char*)0);
 1185   /*}}}*/
 1186   *count=0;
 1187   if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
 1188   fprintf(fp,"# This is a work sheet generated with teapot %s.\n",VERSION);
 1189   for (z=sheet->dimz-1; z>=0; --z)
 1190   {
 1191     for (y=sheet->dimy-1; y>=0; --y)
 1192     {
 1193       for (x=sheet->dimx-1; x>=0; --x)
 1194       {
 1195         if (y==0) if (columnwidth(sheet,x,z)!=DEF_COLUMNWIDTH) fprintf(fp,"W%d %d %d\n",x,z,columnwidth(sheet,x,z));
 1196         if (SHEET(sheet,x,y,z)!=(Cell*)0)
 1197         {
 1198           fprintf(fp,"C%d %d %d ",x,y,z);
 1199           if (SHEET(sheet,x,y,z)->adjust!=AUTOADJUST) fprintf(fp,"A%c ","lrc"[SHEET(sheet,x,y,z)->adjust]);
 1200           if (SHEET(sheet,x,y,z)->label) fprintf(fp,"L%s ",SHEET(sheet,x,y,z)->label);
 1201           if (SHEET(sheet,x,y,z)->precision!=-1) fprintf(fp,"P%d ",SHEET(sheet,x,y,z)->precision);
 1202           if (SHEET(sheet,x,y,z)->shadowed) fprintf(fp,"S ");
 1203           if (SHEET(sheet,x,y,z)->bold) fprintf(fp,"B ");
 1204           if (SHEET(sheet,x,y,z)->underline) fprintf(fp,"U ");
 1205           if (SHEET(sheet,x,y,z)->scientific!=DEF_SCIENTIFIC) fprintf(fp,"E ");
 1206           if (SHEET(sheet,x,y,z)->locked) fprintf(fp,"C ");
 1207           if (SHEET(sheet,x,y,z)->transparent) fprintf(fp,"T ");
 1208           if (SHEET(sheet,x,y,z)->contents)
 1209           {
 1210             char buf[4096];
 1211 
 1212             if (fputc_close(':',fp)==EOF) return strerror(errno);
 1213             print(buf,sizeof(buf),0,1,SHEET(sheet,x,y,z)->scientific,SHEET(sheet,x,y,z)->precision,SHEET(sheet,x,y,z)->contents);
 1214             if (fputs_close(buf,fp)==EOF) return strerror(errno);
 1215           }
 1216           if (SHEET(sheet,x,y,z)->ccontents)
 1217           {
 1218             char buf[4096];
 1219 
 1220             if (fputs_close("\\\n",fp)==EOF) return strerror(errno);
 1221             print(buf,sizeof(buf),0,1,SHEET(sheet,x,y,z)->scientific,SHEET(sheet,x,y,z)->precision,SHEET(sheet,x,y,z)->ccontents);
 1222             if (fputs_close(buf,fp)==EOF) return strerror(errno);
 1223           }
 1224           if (fputc_close('\n',fp)==EOF) return strerror(errno);
 1225           ++*count;
 1226         }
 1227       }
 1228     }
 1229   }
 1230   if (fclose(fp)==EOF) return strerror(errno);
 1231   return (const char*)0;
 1232 }
 1233 /*}}}*/
 1234 /* loadport      -- load from portable text */ /*{{{*/
 1235 const char *loadport(Sheet *sheet, const char *name)
 1236 {
 1237   /* variables */ /*{{{*/
 1238   static char errbuf[80];
 1239   FILE *fp;
 1240   int x,y,z;
 1241   char buf[4096];
 1242   int line;
 1243   const char *ns,*os;
 1244   const char *err;
 1245   int precision;
 1246   char *label;
 1247   Adjust adjust;
 1248   int shadowed;
 1249   int bold;
 1250   int underline;
 1251   int scientific;
 1252   int locked;
 1253   int transparent;
 1254   int ignored;
 1255   Token **contents,**ccontents;
 1256   int width;
 1257   /*}}}*/
 1258 
 1259   if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
 1260   freesheet(sheet,0);
 1261   err=(const char*)0;
 1262   line=1;
 1263   while (fgets(buf,sizeof(buf),fp)!=(char*)0)
 1264   {
 1265     /* remove nl */ /*{{{*/
 1266     width=strlen(buf);
 1267     if (width>0 && buf[width-1]=='\n') buf[--width]='\0';
 1268     /*}}}*/
 1269     switch (buf[0])
 1270     {
 1271       /* C       -- parse cell */ /*{{{*/
 1272       case 'C':
 1273       {
 1274         int cc=0;
 1275 
 1276         if (width>0 && buf[width-1]=='\\') { buf[--width]='\0'; cc=1; }
 1277         adjust=AUTOADJUST;
 1278         precision=-1;
 1279         label=(char*)0;
 1280         contents=(Token**)0;
 1281         ccontents=(Token**)0;
 1282         shadowed=0;
 1283         bold=0;
 1284         underline=0;
 1285         scientific=DEF_SCIENTIFIC;
 1286         locked=0;
 1287         transparent=0;
 1288         ignored=0;
 1289         /* parse x y and z */ /*{{{*/
 1290         os=ns=buf+1;
 1291         x=posnumber(os,&ns);
 1292         if (os==ns)
 1293         {
 1294           sprintf(errbuf,_("Parse error for x position in line %d"),line);
 1295           err=errbuf;
 1296           goto eek;
 1297         }
 1298         while (*ns==' ') ++ns;
 1299         os=ns;
 1300         y=posnumber(os,&ns);
 1301         if (os==ns)
 1302         {
 1303           sprintf(errbuf,_("Parse error for y position in line %d"),line);
 1304           err=errbuf;
 1305           goto eek;
 1306         }
 1307         while (*ns==' ') ++ns;
 1308         os=ns;
 1309         z=posnumber(os,&ns);
 1310         if (os==ns)
 1311         {
 1312           sprintf(errbuf,_("Parse error for z position in line %d"),line);
 1313           err=errbuf;
 1314           goto eek;
 1315         }
 1316         /*}}}*/
 1317         /* parse optional attributes */ /*{{{*/
 1318         do
 1319         {
 1320           while (*ns==' ') ++ns;
 1321           switch (*ns)
 1322           {
 1323             /* A       -- adjustment */ /*{{{*/
 1324             case 'A':
 1325             {
 1326               ++ns;
 1327               switch (*ns)
 1328               {
 1329                 case 'l': adjust=LEFT; ++ns; break;
 1330                 case 'r': adjust=RIGHT; ++ns; break;
 1331                 case 'c': adjust=CENTER; ++ns; break;
 1332                 default:  sprintf(errbuf,_("Parse error for adjustment in line %d"),line); err=errbuf; goto eek;
 1333               }
 1334               break;
 1335             }
 1336             /*}}}*/
 1337             /* L       -- label */ /*{{{*/
 1338             case 'L':
 1339             {
 1340               char buf[1024],*p;
 1341 
 1342               p=buf;
 1343               ++ns;
 1344               while (*ns && *ns!=' ') { *p=*ns; ++p; ++ns; }
 1345               *p='\0';
 1346               label=mystrmalloc(buf);
 1347               break;
 1348             }
 1349             /*}}}*/
 1350             /* P       -- precision */ /*{{{*/
 1351             case 'P':
 1352             {
 1353               os=++ns;
 1354               precision=posnumber(os,&ns);
 1355               if (os==ns)
 1356               {
 1357                 sprintf(errbuf,_("Parse error for precision in line %d"),line);
 1358                 err=errbuf;
 1359                 goto eek;
 1360               }
 1361               break;
 1362             }
 1363             /*}}}*/
 1364             /* S       -- shadowed */ /*{{{*/
 1365             case 'S':
 1366             {
 1367               if (x==0)
 1368               {
 1369                 sprintf(errbuf,_("Trying to shadow cell (%d,%d,%d) in line %d"),x,y,z,line);
 1370                 err=errbuf;
 1371                 goto eek;
 1372               }
 1373               ++ns;
 1374               shadowed=1;
 1375               break;
 1376             }
 1377             /*}}}*/
 1378             /* U       -- underline */ /*{{{*/
 1379             case 'U':
 1380             {
 1381               if (x==0)
 1382               {
 1383                 sprintf(errbuf,_("Trying to underline cell (%d,%d,%d) in line %d"),x,y,z,line);
 1384                 err=errbuf;
 1385                 goto eek;
 1386               }
 1387               ++ns;
 1388               underline=1;
 1389               break;
 1390             }
 1391             /*}}}*/
 1392             /* B       -- bold */ /*{{{*/
 1393             case 'B':
 1394             {
 1395               if (x==0)
 1396               {
 1397                 sprintf(errbuf,_("Trying to bold cell (%d,%d,%d) in line %d"),x,y,z,line);
 1398                 err=errbuf;
 1399                 goto eek;
 1400               }
 1401               ++ns;
 1402               bold=1;
 1403               break;
 1404             }
 1405             /*}}}*/
 1406             /* E       -- scientific */ /*{{{*/
 1407             case 'E':
 1408             {
 1409               ++ns;
 1410               scientific=1;
 1411               break;
 1412             }
 1413             /*}}}*/
 1414             /* O       -- locked */ /*{{{*/
 1415             case 'O':
 1416             {
 1417               ++ns;
 1418               locked=1;
 1419               break;
 1420             }
 1421             /*}}}*/
 1422             /* T       -- transparent */ /*{{{*/
 1423             case 'T':
 1424             {
 1425               ++ns;
 1426               transparent=1;
 1427               break;
 1428             }
 1429             /*}}}*/
 1430             /* I       -- ignored */ /*{{{*/
 1431             case 'I':
 1432             {
 1433               ++ns;
 1434               ignored=1;
 1435               break;
 1436             }
 1437             /*}}}*/
 1438             /* : \0    -- do nothing */ /*{{{*/
 1439             case ':':
 1440             case '\0': break;
 1441             /*}}}*/
 1442             /* default -- error */ /*{{{*/
 1443             default: sprintf(errbuf,_("Invalid option %c in line %d"),*ns,line); err=errbuf; goto eek;
 1444             /*}}}*/
 1445           }
 1446         } while (*ns!=':' && *ns!='\0');
 1447         /*}}}*/
 1448         /* convert remaining string into token sequence  */ /*{{{*/
 1449         if (*ns)
 1450         {
 1451           ++ns;
 1452           contents=scan(&ns);
 1453           if (contents==(Token**)0)
 1454           {
 1455             tvecfree(contents);
 1456             sprintf(errbuf,_("Expression syntax error in line %d"),line);
 1457             err=errbuf;
 1458             goto eek;
 1459           }
 1460         }
 1461         /*}}}*/
 1462         /* convert remaining string into token sequence */ /*{{{*/
 1463         if (cc && fgets(buf,sizeof(buf),fp)!=(char*)0)
 1464         {
 1465           ++line;
 1466           /* remove nl */ /*{{{*/
 1467           width=strlen(buf);
 1468           if (width>0 && buf[width-1]=='\n') buf[width-1]='\0';
 1469           /*}}}*/
 1470           ns=buf;
 1471           ccontents=scan(&ns);
 1472           if (ccontents==(Token**)0)
 1473           {
 1474             tvecfree(ccontents);
 1475             sprintf(errbuf,_("Expression syntax error in line %d"),line);
 1476             err=errbuf;
 1477             goto eek;
 1478           }
 1479         }
 1480         /*}}}*/
 1481         initcell(sheet,x,y,z);
 1482         SHEET(sheet,x,y,z)->adjust=adjust;
 1483         SHEET(sheet,x,y,z)->label=label;
 1484         SHEET(sheet,x,y,z)->precision=precision;
 1485         SHEET(sheet,x,y,z)->shadowed=shadowed;
 1486         SHEET(sheet,x,y,z)->bold=bold;
 1487         SHEET(sheet,x,y,z)->underline=underline;
 1488         SHEET(sheet,x,y,z)->scientific=scientific;
 1489         SHEET(sheet,x,y,z)->locked=locked;
 1490         SHEET(sheet,x,y,z)->transparent=transparent;
 1491         SHEET(sheet,x,y,z)->ignored=ignored;
 1492         SHEET(sheet,x,y,z)->contents=contents;
 1493         SHEET(sheet,x,y,z)->ccontents=ccontents;
 1494         break;
 1495       }
 1496       /*}}}*/
 1497       /* W       -- column width */ /*{{{*/
 1498       case 'W':
 1499       {
 1500         /* parse x and z */ /*{{{*/
 1501         os=ns=buf+1;
 1502         x=posnumber(os,&ns);
 1503         if (os==ns)
 1504         {
 1505           sprintf(errbuf,_("Parse error for x position in line %d"),line);
 1506           err=errbuf;
 1507           goto eek;
 1508         }
 1509         while (*ns==' ') ++ns;
 1510         os=ns;
 1511         z=posnumber(os,&ns);
 1512         if (os==ns)
 1513         {
 1514           sprintf(errbuf,_("Parse error for z position in line %d"),line);
 1515           err=errbuf;
 1516           goto eek;
 1517         }
 1518         /*}}}*/
 1519         /* parse width */ /*{{{*/
 1520         while (*ns==' ') ++ns;
 1521         os=ns;
 1522         width=posnumber(os,&ns);
 1523         if (os==ns)
 1524         {
 1525           sprintf(errbuf,_("Parse error for width in line %d"),line);
 1526           err=errbuf;
 1527           goto eek;
 1528         }
 1529         /*}}}*/
 1530         setwidth(sheet,x,z,width);
 1531         break;
 1532       }
 1533       /*}}}*/
 1534       /* #       -- comment */ /*{{{*/
 1535       case '#': break;
 1536       /*}}}*/
 1537       /* default -- error */ /*{{{*/
 1538       default:
 1539       {
 1540         sprintf(errbuf,_("Unknown tag %c in line %d"),buf[0],line);
 1541         err=errbuf;
 1542         goto eek;
 1543       }
 1544       /*}}}*/
 1545     }
 1546     ++line;
 1547   }
 1548   eek:
 1549   if (fclose(fp)==EOF && err==(const char*)0) err=strerror(errno);
 1550   sheet->changed=0;
 1551   cachelabels(sheet);
 1552   forceupdate(sheet);
 1553   return err;
 1554 }
 1555 /*}}}*/
 1556 /* loadxdr       -- load a spread sheet in XDR */ /*{{{*/
 1557 const char *loadxdr(Sheet *sheet, const char *name)
 1558 {
 1559   /* variables */ /*{{{*/
 1560   FILE *fp;
 1561   XDR xdrs;
 1562   int x,y,z;
 1563   int width;
 1564   int u;
 1565   int olderror;
 1566   /*}}}*/
 1567 
 1568   if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
 1569   xdrstdio_create(&xdrs,fp,XDR_DECODE);
 1570   if (!xdr_magic(&xdrs))
 1571   {
 1572 #if 0
 1573     xdr_destroy(&xdrs);
 1574     fclose(fp);
 1575     return _("This is not a teapot worksheet in XDR format");
 1576 #else
 1577     xdr_destroy(&xdrs);
 1578     rewind(fp);
 1579     xdrstdio_create(&xdrs,fp,XDR_DECODE);
 1580 #endif
 1581   }
 1582   freesheet(sheet,0);
 1583   while (xdr_int(&xdrs,&u)) switch (u)
 1584   {
 1585     /* 0       -- column width element */ /*{{{*/
 1586     case 0:
 1587     {
 1588       if (xdr_column(&xdrs,&x,&z,&width)==0)
 1589       {
 1590         olderror=errno;
 1591         xdr_destroy(&xdrs);
 1592         (void)fclose(fp);
 1593         return strerror(olderror);
 1594       }
 1595       setwidth(sheet,x,z,width);
 1596       break;
 1597     }
 1598     /*}}}*/
 1599     /* 1       -- cell element */ /*{{{*/
 1600     case 1:
 1601     {
 1602       if (xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0)
 1603       {
 1604         olderror=errno;
 1605         xdr_destroy(&xdrs);
 1606         (void)fclose(fp);
 1607         return strerror(olderror);
 1608       }
 1609       initcell(sheet,x,y,z);
 1610       if (xdr_cell(&xdrs,SHEET(sheet,x,y,z))==0)
 1611       {
 1612         freecell(sheet,x,y,z);
 1613         olderror=errno;
 1614         xdr_destroy(&xdrs);
 1615         (void)fclose(fp);
 1616         return strerror(olderror);
 1617       }
 1618       break;
 1619     }
 1620     /*}}}*/
 1621     /* default -- should not happen */ /*{{{*/
 1622     default:
 1623     {
 1624       xdr_destroy(&xdrs);
 1625       fclose(fp);
 1626       sheet->changed=0;
 1627       cachelabels(sheet);
 1628       forceupdate(sheet);
 1629       return _("Invalid record, loading aborted");
 1630     }
 1631     /*}}}*/
 1632   }
 1633   xdr_destroy(&xdrs);
 1634   if (fclose(fp)==EOF) return strerror(errno);
 1635   sheet->changed=0;
 1636   cachelabels(sheet);
 1637   forceupdate(sheet);
 1638   redraw_sheet(sheet);
 1639   return (const char*)0;
 1640 }
 1641 /*}}}*/
 1642 /* loadcsv       -- load/merge CSVs */ /*{{{*/
 1643 const char *loadcsv(Sheet *sheet, const char *name)
 1644 {
 1645   /* variables */ /*{{{*/
 1646   FILE *fp;
 1647   Token **t;
 1648   const char *err;
 1649   int line,x;
 1650   char ln[4096];
 1651   const char *str;
 1652   double value;
 1653   long lvalue;
 1654   int separator = 0;
 1655   /*}}}*/
 1656 
 1657   if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
 1658   err=(const char*)0;
 1659   for (x=0,line=1; fgets(ln,sizeof(ln),fp); ++line)
 1660   {
 1661     const char *s;
 1662     const char *cend;
 1663 
 1664     if (!separator) { /* FIXME: find a better way to autodetect */
 1665         int ccnt = 0, scnt = 0;
 1666         char *pos = ln;
 1667         while ((pos = strchr(pos, ','))) pos++, ccnt++;
 1668         pos = ln;
 1669         while ((pos = strchr(pos, ';'))) pos++, scnt++;
 1670         if (ccnt || scnt) separator = 1;
 1671         csv_setopt(scnt > ccnt);
 1672     }
 1673 
 1674     s=cend=ln;
 1675     x=0;
 1676     do
 1677     {
 1678       t=malloc(2*sizeof(Token*));
 1679       t[0]=malloc(sizeof(Token));
 1680       t[1]=(Token*)0;
 1681       lvalue=csv_long(s,&cend);
 1682       if (s!=cend) /* ok, it is a integer */ /*{{{*/
 1683       {
 1684         t[0]->type=INT;
 1685         t[0]->u.integer=lvalue;
 1686         putcont(sheet, sheet->curx+x, sheet->cury+line-1, sheet->curz, t, 0);
 1687       }
 1688       /*}}}*/
 1689       else
 1690       {
 1691         value=csv_double(s,&cend);
 1692         if (s!=cend) /* ok, it is a double */ /*{{{*/
 1693         {
 1694           t[0]->type=FLOAT;
 1695           t[0]->u.flt=value;
 1696           putcont(sheet, sheet->curx+x, sheet->cury+line-1, sheet->curz, t, 0);
 1697         }
 1698         /*}}}*/
 1699         else
 1700         {
 1701           str=csv_string(s,&cend);
 1702           if (s!=cend) /* ok, it is a string */ /*{{{*/
 1703           {
 1704             t[0]->type=STRING;
 1705             t[0]->u.string=mystrmalloc(str);
 1706             putcont(sheet, sheet->curx+x, sheet->cury+line-1, sheet->curz, t, 0);
 1707           }
 1708           /*}}}*/
 1709           else
 1710           {
 1711             tvecfree(t);
 1712             csv_separator(s,&cend);
 1713             while (s==cend && *s && *s!='\n')
 1714             {
 1715               err=_("unknown values ignored");
 1716               csv_separator(++s,&cend);
 1717             }
 1718             /* else it is nothing, which does not need to be stored :) */
 1719           }
 1720         }
 1721       }
 1722     } while (s!=cend ? s=cend,++x,1 : 0);
 1723   }
 1724   fclose(fp);
 1725   return err;
 1726 }
 1727 /*}}}*/
 1728 /* insertcube    -- insert a block */ /*{{{*/
 1729 void insertcube(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction ins)
 1730 {
 1731   /* variables */ /*{{{*/
 1732   int x,y,z;
 1733   /*}}}*/
 1734 
 1735   switch (ins)
 1736   {
 1737     /* IN_X    */ /*{{{*/
 1738     case IN_X:
 1739     {
 1740       int right;
 1741 
 1742       right=sheet->dimx+x2-x1;
 1743       for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) for (x=right; x>x2; --x)
 1744       {
 1745         resize(sheet,x,y,z);
 1746         SHEET(sheet,x,y,z)=SHEET(sheet,x-(x2-x1+1),y,z);
 1747         SHEET(sheet,x-(x2-x1+1),y,z)=(Cell*)0;
 1748       }
 1749       break;
 1750     }
 1751     /*}}}*/
 1752     /* IN_Y    */ /*{{{*/
 1753     case IN_Y:
 1754     {
 1755       int down;
 1756 
 1757       down=sheet->dimy+y2-y1;
 1758       for (z=z1; z<=z2; ++z) for (x=x1; x<=x2; ++x) for (y=down; y>y2; --y)
 1759       {
 1760         resize(sheet,x,y,z);
 1761         SHEET(sheet,x,y,z)=SHEET(sheet,x,y-(y2-y1+1),z);
 1762         SHEET(sheet,x,y-(y2-y1+1),z)=(Cell*)0;
 1763       }
 1764       break;
 1765     }
 1766     /*}}}*/
 1767     /* IN_Z */ /*{{{*/
 1768     case IN_Z:
 1769     {
 1770       int bottom;
 1771 
 1772       bottom=sheet->dimz+z2-z1;
 1773       for (y=y1; y<=y2; ++y) for (x=x1; x<=x2; ++x) for (z=bottom; z>z2; --z)
 1774       {
 1775         resize(sheet,x,y,z);
 1776         SHEET(sheet,x,y,z)=SHEET(sheet,x,y,z-(z2-z1+1));
 1777         SHEET(sheet,x,y,z-(z2-z1+1))=(Cell*)0;
 1778       }
 1779       break;
 1780     }
 1781     /*}}}*/
 1782     /* default */ /*{{{*/
 1783     default: assert(0);
 1784     /*}}}*/
 1785   }
 1786   sheet->changed=1;
 1787   cachelabels(sheet);
 1788   forceupdate(sheet);
 1789 }
 1790 /*}}}*/
 1791 /* deletecube    -- delete a block */ /*{{{*/
 1792 void deletecube(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction del)
 1793 {
 1794   /* variables */ /*{{{*/
 1795   int x,y,z;
 1796   /*}}}*/
 1797 
 1798   /* free cells in marked block */ /*{{{*/
 1799   for (x=x1; x<=x2; ++x)
 1800   for (y=y1; y<=y2; ++y)
 1801   for (z=z1; z<=z2; ++z)
 1802   freecell(sheet,x,y,z);
 1803   /*}}}*/
 1804   switch (del)
 1805   {
 1806     /* IN_X */ /*{{{*/
 1807     case IN_X:
 1808     {
 1809       for (z=z1; z<=z2; ++z) for (y=y1; y<=y2; ++y) for (x=x1; x<=sheet->dimx-(x2-x1+1); ++x)
 1810       {
 1811         if (x+(x2-x1+1)<sheet->dimx && y<sheet->dimy && z<sheet->dimz)
 1812         {
 1813           SHEET(sheet,x,y,z)=SHEET(sheet,x+(x2-x1+1),y,z);
 1814           SHEET(sheet,x+(x2-x1+1),y,z)=(Cell*)0;
 1815         }
 1816       }
 1817       break;
 1818     }
 1819     /*}}}*/
 1820     /* IN_Y */ /*{{{*/
 1821     case IN_Y:
 1822     {
 1823       for (z=z1; z<=z2; ++z) for (x=x1; x<=x2; ++x) for (y=y1; y<=sheet->dimy-(y2-y1+1); ++y)
 1824       {
 1825         if (x<sheet->dimx && y+(y2-y1+1)<sheet->dimy && z<sheet->dimz)
 1826         {
 1827           SHEET(sheet,x,y,z)=SHEET(sheet,x,y+(y2-y1+1),z);
 1828           SHEET(sheet,x,y+(y2-y1+1),z)=(Cell*)0;
 1829         }
 1830       }
 1831       break;
 1832     }
 1833     /*}}}*/
 1834     /* IN_Z */ /*{{{*/
 1835     case IN_Z:
 1836     {
 1837       for (y=y1; y<=y2; ++y) for (x=x1; x<=x2; ++x) for (z=z1; z<=sheet->dimz-(z2-z1+1); ++z)
 1838       {
 1839         if (x<sheet->dimx && y<sheet->dimy && z+(z2-z1+1)<sheet->dimz)
 1840         {
 1841           SHEET(sheet,x,y,z)=SHEET(sheet,x,y,z+(z2-z1+1));
 1842           SHEET(sheet,x,y,z+(z2-z1+1))=(Cell*)0;
 1843         }
 1844       }
 1845       break;
 1846     }
 1847     /*}}}*/
 1848     /* default */ /*{{{*/
 1849     default: assert(0);
 1850     /*}}}*/
 1851   }
 1852   sheet->changed=1;
 1853   cachelabels(sheet);
 1854   forceupdate(sheet);
 1855 }
 1856 /*}}}*/
 1857 /* moveblock     -- move a block */ /*{{{*/
 1858 void moveblock(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int copy)
 1859 {
 1860   /* variables */ /*{{{*/
 1861   int dirx, diry, dirz;
 1862   int widx, widy, widz;
 1863   int x, y, z;
 1864   int xf, xt, yf, yt, zf, zt;
 1865   /*}}}*/
 1866 
 1867   if (x1==x3 && y1==y3 && z1==z3) return;
 1868   widx=(x2-x1);
 1869   widy=(y2-y1);
 1870   widz=(z2-z1);
 1871   if (x3>x1) { dirx=-1; xf=widx; xt=-1; } else { dirx=1; xf=0; xt=widx+1; }
 1872   if (y3>y1) { diry=-1; yf=widy; yt=-1; } else { diry=1; yf=0; yt=widy+1; }
 1873   if (z3>z1) { dirz=-1; zf=widz; zt=-1; } else { dirz=1; zf=0; zt=widz+1; }
 1874   for (x=xf; x!=xt; x+=dirx)
 1875   for (y=yf; y!=yt; y+=diry)
 1876   for (z=zf; z!=zt; z+=dirz)
 1877   {
 1878     if (copy)
 1879     {
 1880       copycell(sheet,x1+x,y1+y,z1+z,sheet,x3+x,y3+y,z3+z);
 1881     }
 1882     else
 1883     {
 1884       if (x1+x<sheet->dimx && y1+y<sheet->dimy && z1+z<sheet->dimz)
 1885       {
 1886         resize(sheet,x3+x,y3+y,z3+z);
 1887         SHEET(sheet,x3+x,y3+y,z3+z)=SHEET(sheet,x1+x,y1+y,z1+z);
 1888         SHEET(sheet,x1+x,y1+y,z1+z)=(Cell*)0;
 1889       }
 1890       else
 1891       {
 1892         freecell(sheet,x3+x,y3+y,z3+z);
 1893       }
 1894     }
 1895   }
 1896   sheet->changed=1;
 1897   cachelabels(sheet);
 1898   forceupdate(sheet);
 1899 }
 1900 /*}}}*/
 1901 /* sortblock     -- sort a block */ /*{{{*/
 1902 /* Notes */ /*{{{*/
 1903 /*
 1904 The idea is to sort a block of cells in one direction by swapping the
 1905 planes which are canonical to the sort key vectors.  An example is to
 1906 sort a two dimensional block line-wise with one column as sort key.
 1907 You can have multiple sort keys which all have the same direction and
 1908 you can sort a cube plane-wise.
 1909 */
 1910 /*}}}*/
 1911 const char *sortblock(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction dir, Sortkey *sk, size_t sklen)
 1912 {
 1913   /* variables */ /*{{{*/
 1914   int x,y,z;
 1915   int incx=0,incy=0,incz=0;
 1916   int distx,disty,distz;
 1917   int i,r=-3,norel,work;
 1918   /*}}}*/
 1919 
 1920   /* asserts */ /*{{{*/
 1921   assert(sklen>0);
 1922   assert(x1>=0);
 1923   assert(x2>=0);
 1924   assert(y1>=0);
 1925   assert(y2>=0);
 1926   assert(z1>=0);
 1927   assert(z2>=0);
 1928   /*}}}*/
 1929   norel=0;
 1930   posorder(&x1,&x2);
 1931   posorder(&y1,&y2);
 1932   posorder(&z1,&z2);
 1933   distx=(x2-x1+1);
 1934   disty=(y2-y1+1);
 1935   distz=(z2-z1+1);
 1936   switch (dir)
 1937   {
 1938     case IN_X: incx=1; --x2; incy=0; incz=0; distx=1; break;
 1939     case IN_Y: incx=0; incy=1; --y2; incz=0; disty=1; break;
 1940     case IN_Z: incx=0; incy=0; incz=1; --z2; distz=1; break;
 1941     default: assert(0);
 1942   }
 1943   assert(incx || incy || incz);
 1944   do
 1945   {
 1946     work=0;
 1947     for (x=x1,y=y1,z=z1; x<=x2&&y<=y2&&z<=z2; x+=incx,y+=incy,z+=incz)
 1948     {
 1949       for (i=0; i<sklen; ++i)
 1950       {
 1951         r=cmpcell(sheet,x+sk[i].x,y+sk[i].y,z+sk[i].z,sheet,x+incx+sk[i].x,y+incy+sk[i].y,z+incz+sk[i].z,sk[i].sortkey);
 1952         if (r==2) norel=1;
 1953         else if (r==-1 || r==1) break;
 1954         else assert(r==0);
 1955       }
 1956       if (r==1)
 1957       {
 1958         swapblock(sheet,dir==IN_X ? x : x1,dir==IN_Y ? y : y1,dir==IN_Z ? z : z1,sheet,dir==IN_X ? x+incx : x1,dir==IN_Y ? y+incy : y1,dir==IN_Z ? z+incz : z1,distx,disty,distz);
 1959         work=1;
 1960       }
 1961     }
 1962     x2-=incx;
 1963     y2-=incy;
 1964     z2-=incz;
 1965   } while (work);
 1966   cachelabels(sheet);
 1967   forceupdate(sheet);
 1968   sheet->changed=1;
 1969   if (norel) return _("uncomparable elements");
 1970   else return (const char*)0;
 1971 }
 1972 /*}}}*/
 1973 /* mirrorblock   -- mirror a block */ /*{{{*/
 1974 void mirrorblock(Sheet *sheet, int x1, int y1, int z1, int x2, int y2, int z2, Direction dir)
 1975 {
 1976   switch (dir)
 1977   {
 1978     case IN_X: /* left-right */ /*{{{*/
 1979     {
 1980       int x,middle=(x2-x1+1)/2;
 1981       for (x=0; x<middle; ++x)
 1982       {
 1983         swapblock(sheet,x1+x,y1,z1,sheet,x2-x,y1,z1, 1,y2-y1+1,z2-z1+1);
 1984       }
 1985       break;
 1986     }
 1987     /*}}}*/
 1988     case IN_Y: /* upside-down */ /*{{{*/
 1989     {
 1990       int y,middle=(y2-y1+1)/2;
 1991       for (y=0; y<middle; ++y)
 1992       {
 1993         swapblock(sheet,x1,y1+y,z1,sheet,x1,y2-y,z1, x2-x1+1,1,z2-z1+1);
 1994       }
 1995       break;
 1996     }
 1997     /*}}}*/
 1998     case IN_Z: /* front-back */ /*{{{*/
 1999     {
 2000       int z,middle=(z2-z1+1)/2;
 2001       for (z=0; z<middle; ++z)
 2002       {
 2003         swapblock(sheet,x1,y1,z1+z,sheet,x1,y1,z2-z, x2-x1+1,y2-y1+1,1);
 2004       }
 2005       break;
 2006     }
 2007     /*}}}*/
 2008     default: assert(0);
 2009   }
 2010   sheet->changed=1;
 2011   cachelabels(sheet);
 2012   forceupdate(sheet);
 2013 }
 2014 /*}}}*/