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 */ /*{{{*/
copycell(Sheet * sheet1,int x1,int y1,int z1,Sheet * sheet2,int x2,int y2,int z2)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 */ /*{{{*/
swapblock(Sheet * sheet1,int x1,int y1,int z1,Sheet * sheet2,int x2,int y2,int z2,int xdist,int ydist,int zdist)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 /*}}}*/
cmpcell(Sheet * sheet1,int x1,int y1,int z1,Sheet * sheet2,int x2,int y2,int z2,int sortkey)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 */ /*{{{*/
resize(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
initcell(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
cachelabels(Sheet * sheet)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 */ /*{{{*/
freesheet(Sheet * sheet,int all)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 */ /*{{{*/
forceupdate(Sheet * sheet)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 */ /*{{{*/
freecell(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
columnwidth(Sheet * sheet,int x,int z)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 */ /*{{{*/
setwidth(Sheet * sheet,int x,int z,int width)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 */ /*{{{*/
cellwidth(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
putcont(Sheet * sheet,int x,int y,int z,Token ** t,int c)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 */ /*{{{*/
getcont(Sheet * sheet,int x,int y,int z,int c)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 */ /*{{{*/
getvalue(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
update(Sheet * sheet)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 */ /*{{{*/
geterror(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
printvalue(char * s,size_t size,size_t chars,int quote,int scientific,int precision,Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
getadjust(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
setadjust(Sheet * sheet,int x,int y,int z,Adjust adjust)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 */ /*{{{*/
shadow(Sheet * sheet,int x,int y,int z,int yep)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? */ /*{{{*/
shadowed(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
bold(Sheet * sheet,int x,int y,int z,int yep)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? */ /*{{{*/
isbold(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
underline(Sheet * sheet,int x,int y,int z,int yep)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? */ /*{{{*/
underlined(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
lockcell(Sheet * sheet,int x,int y,int z,int yep)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? */ /*{{{*/
locked(Sheet * sheet,int x,int y,int z)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? */ /*{{{*/
transparent(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
maketrans(Sheet * sheet,int x,int y,int z,int yep)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 */ /*{{{*/
igncell(Sheet * sheet,int x,int y,int z,int yep)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? */ /*{{{*/
ignored(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
clk(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
setscientific(Sheet * sheet,int x,int y,int z,int yep)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? */ /*{{{*/
getscientific(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
setprecision(Sheet * sheet,int x,int y,int z,int 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 */ /*{{{*/
getprecision(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
getlabel(Sheet * sheet,int x,int y,int z)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 */ /*{{{*/
setlabel(Sheet * sheet,int x,int y,int z,const char * buf,int update)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 */ /*{{{*/
findlabel(Sheet * sheet,const char * 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 */ /*{{{*/
relabel(Sheet * sheet,const char * oldlabel,const char * newlabel,int x,int y,int z)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 */ /*{{{*/
savexdr(Sheet * sheet,const char * name,unsigned int * count)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 */ /*{{{*/
savetbl(Sheet * sheet,const char * name,int body,int x1,int y1,int z1,int x2,int y2,int z2,unsigned int * count)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 */ /*{{{*/
savetext(Sheet * sheet,const char * name,int x1,int y1,int z1,int x2,int y2,int z2,unsigned int * count)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 */ /*{{{*/
savecsv(Sheet * sheet,const char * name,char sep,int x1,int y1,int z1,int x2,int y2,int z2,unsigned int * count)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 */ /*{{{*/
saveport(Sheet * sheet,const char * name,unsigned int * count)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 */ /*{{{*/
loadport(Sheet * sheet,const char * name)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 */ /*{{{*/
loadxdr(Sheet * sheet,const char * name)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 */ /*{{{*/
loadcsv(Sheet * sheet,const char * name)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 */ /*{{{*/
insertcube(Sheet * sheet,int x1,int y1,int z1,int x2,int y2,int z2,Direction ins)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 */ /*{{{*/
deletecube(Sheet * sheet,int x1,int y1,int z1,int x2,int y2,int z2,Direction del)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 */ /*{{{*/
moveblock(Sheet * sheet,int x1,int y1,int z1,int x2,int y2,int z2,int x3,int y3,int z3,int copy)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 /*}}}*/
sortblock(Sheet * sheet,int x1,int y1,int z1,int x2,int y2,int z2,Direction dir,Sortkey * sk,size_t sklen)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 */ /*{{{*/
mirrorblock(Sheet * sheet,int x1,int y1,int z1,int x2,int y2,int z2,Direction dir)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 /*}}}*/
2015