"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 /*}}}*/