1 /* #includes */ /*{{{C}}}*//*{{{*/
2 #ifndef NO_POSIX_SOURCE
3 #undef  _POSIX_SOURCE
4 #define _POSIX_SOURCE   1
5 #undef  _POSIX_C_SOURCE
6 #define _POSIX_C_SOURCE 2
7 #undef  _XOPEN_SOURCE
8 #define _XOPEN_SOURCE 500
9 #endif
10 
11 #ifdef DMALLOC
12 #include "dmalloc.h"
13 #endif
14 
15 #include <assert.h>
16 #include <ctype.h>
17 #include <float.h>
18 #include <limits.h>
19 #include <locale.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 extern char *optarg;
24 extern int optind,opterr,optopt;
25 int getopt(int argc, char * const *argv, const char *optstring);
26 #include <string.h>
27 #include <unistd.h>
28 
29 
30 #include "default.h"
31 #include "display.h"
32 #include "eval.h"
33 #include "htmlio.h"
34 #include "latex.h"
35 #include "context.h"
36 #include "main.h"
37 #include "misc.h"
38 #include "sc.h"
39 #include "scanner.h"
40 #include "utf8.h"
41 #include "parser.h"
42 #include "sheet.h"
43 #include "wk1.h"
44 /*}}}*/
45 
46 /* variables */ /*{{{*/
47 char helpfile[PATH_MAX];
48 int batch=0;
49 unsigned int batchln=0;
50 int def_precision=DEF_PRECISION;
51 int quote=0;
52 int header=1;
53 static int usexdr=1;
54 /*}}}*/
55 
get_mark(Sheet * sheet,int * x1,int * x2,int * y1,int * y2,int * z1,int * z2,int everything)56 static void get_mark(Sheet *sheet, int *x1, int *x2, int *y1, int *y2, int *z1, int *z2, int everything)
57 {
58     if (sheet->marking) {
59         posorder(&sheet->mark1x, &sheet->mark2x);
60         posorder(&sheet->mark1y, &sheet->mark2y);
61         posorder(&sheet->mark1z, &sheet->mark2z);
62         sheet->marking = 0;
63     }
64     if (x1) {
65         if (sheet->mark1x >= 0) {
66             *x1 = sheet->mark1x;
67             *x2 = sheet->mark2x;
68             *y1 = sheet->mark1y;
69             *y2 = sheet->mark2y;
70             *z1 = sheet->mark1z;
71             *z2 = sheet->mark2z;
72         } else if (everything) {
73             *x1 = 0;
74             *x2 = sheet->dimx-1;
75             *y1 = 0;
76             *y2 = sheet->dimy-1;
77             *z1 = 0;
78             *z2 = sheet->dimz-1;
79         } else {
80             *x1 = *x2 = sheet->curx;
81             *y1 = *y2 = sheet->cury;
82             *z1 = *z2 = sheet->curz;
83         }
84     }
85 }
86 
moveto(Sheet * sheet,int x,int y,int z)87 void moveto(Sheet *sheet, int x, int y, int z)
88 {
89     int need_redraw = 0;
90     int xdir = x > sheet->curx?1:-1;
91 
92     if (x >= 0) sheet->curx = x;
93     if (y >= 0) sheet->cury = y;
94     if (z >= 0) need_redraw++, sheet->curz = z;
95     while (sheet->curx > 0 && shadowed(sheet, sheet->curx, sheet->cury, sheet->curz)) sheet->curx += xdir;
96 
97     if (sheet->marking) {
98         sheet->mark2x = sheet->curx;
99         sheet->mark2y = sheet->cury;
100         sheet->mark2z = sheet->curz;
101     }
102 
103     if (sheet->curx <= sheet->offx && sheet->offx) need_redraw++, sheet->offx = (sheet->curx?sheet->curx-1:0);
104     if (sheet->cury <= sheet->offy && sheet->offy) need_redraw++, sheet->offy = (sheet->cury?sheet->cury-1:0);
105     if (sheet->curx >= sheet->offx+sheet->maxx) need_redraw++, sheet->offx = sheet->curx-sheet->maxx+2;
106     if (sheet->cury >= sheet->offy+sheet->maxy) need_redraw++, sheet->offy = sheet->cury-sheet->maxy+2;
107 
108     if (need_redraw) redraw_sheet(sheet);
109     else if (x != sheet->curx || y != sheet->cury || z != sheet->curz) redraw_cell(sheet, sheet->curx, sheet->cury, sheet->curz);
110 }
111 
relmoveto(Sheet * sheet,int x,int y,int z)112 void relmoveto(Sheet *sheet, int x, int y, int z)
113 {
114     moveto(sheet, sheet->curx+x, sheet->cury+y, (z?sheet->curz+z:-1));
115 }
116 
117 /* line_numedit   -- number line editor function */ /*{{{*/
line_numedit(int * n,const char * prompt,size_t * x,size_t * offx)118 static int line_numedit(int *n, const char *prompt, size_t *x, size_t *offx)
119 {
120   /* variables */ /*{{{*/
121   char buf[20];
122   const char *s;
123   Token **t;
124   int c;
125   /*}}}*/
126 
127   /* asserts */ /*{{{*/
128   assert(prompt!=(char*)0);
129   assert(x!=(size_t*)0);
130   assert(offx!=(size_t*)0);
131   /*}}}*/
132   t=(Token**)0;
133   sprintf(buf,"%d",*n);
134   s=buf+strlen(buf);
135   do
136   {
137     tvecfree(t);
138     *x=s-buf;
139     if ((c=line_edit((Sheet*)0,buf,sizeof(buf),prompt,x,offx))<0) return c;
140     s=buf;
141     t=scan(&s);
142   } while ((*s!='\0' && t==(Token**)0) || !(t!=(Token**)0 && ((*t)==(Token*)0 || ((*t)->type==INT && (*t)->u.integer>=0 && *(t+1)==(Token*)0))));
143   if (t==(Token**)0 || *t==(Token*)0) *n=-1;
144   else *n=(*t)->u.integer;
145   tvecfree(t);
146   return 0;
147 }
148 /*}}}*/
149 /* line_lidedit   -- label identifier line editor function */ /*{{{*/
line_idedit(char * ident,size_t size,const char * prompt,size_t * x,size_t * offx)150 static int line_idedit(char *ident, size_t size, const char *prompt, size_t *x, size_t *offx)
151 {
152   /* variables */ /*{{{*/
153   const char *s;
154   Token **t;
155   int c;
156   /*}}}*/
157 
158   t=(Token**)0;
159   s=ident+strlen(ident);
160   do
161   {
162     tvecfree(t);
163     *x=s-ident;
164     if ((c=line_edit((Sheet*)0,ident,size,prompt,x,offx))<0) return c;
165     s=ident;
166     t=scan(&s);
167   } while ((*s!='\0' && t==(Token**)0) || !(t!=(Token**)0 && ((*t)==(Token*)0 || ((*t)->type==LIDENT && *(t+1)==(Token*)0))));
168   tvecfree(t);
169   return 0;
170 }
171 /*}}}*/
172 /* doanyway       -- ask if action should be done despite unsaved changes */ /*{{{*/
doanyway(Sheet * sheet,const char * msg)173 int doanyway(Sheet *sheet, const char *msg)
174 {
175   int result;
176 
177   if (sheet->changed) {
178     result=line_ok(msg,0);
179     if (result < 0) return 0;
180     return result;
181   }
182   return 1;
183 }
184 /*}}}*/
185 
186 /* do_edit        -- set or modify cell contents */ /*{{{*/
do_edit(Sheet * cursheet,Key c,const char * expr,int clocked)187 static int do_edit(Sheet *cursheet, Key c, const char *expr, int clocked)
188 {
189   /* variables */ /*{{{*/
190   char buf[1024];
191   const char *s;
192   size_t x,offx;
193   int curx,cury,curz;
194   Token **t;
195   /*}}}*/
196 
197   if (locked(cursheet,cursheet->curx,cursheet->cury,cursheet->curz)) line_msg(_("Edit cell:"),_("Cell is locked"));
198   else
199   {
200     curx=cursheet->curx;
201     cury=cursheet->cury;
202     curz=cursheet->curz;
203     if (expr)
204     {
205       s=expr;
206       t=scan(&s);
207       if (*s!='\0' && t==(Token**)0) line_msg(clocked ? _("Clocked cell contents:") : _("Cell contents:"),"XXX invalid expression");
208     }
209     else
210     {
211       offx=0;
212       if (c==K_NONE)
213       {
214         print(buf,sizeof(buf),0,1,getscientific(cursheet,cursheet->curx,cursheet->cury,cursheet->curz),-1,getcont(cursheet,cursheet->curx,cursheet->cury,cursheet->curz,clocked));
215         s=buf+strlen(buf);
216       }
217       else if (c==K_BACKSPACE)
218       {
219         print(buf,sizeof(buf),0,1,getscientific(cursheet,cursheet->curx,cursheet->cury,cursheet->curz),-1,getcont(cursheet,cursheet->curx,cursheet->cury,cursheet->curz,clocked));
220         if (strlen(buf)) *mbspos(buf+strlen(buf),-1)='\0';
221         s=buf+strlen(buf);
222       }
223       else if (c==K_DC)
224       {
225         print(buf,sizeof(buf),0,1,getscientific(cursheet,cursheet->curx,cursheet->cury,cursheet->curz),-1,getcont(cursheet,cursheet->curx,cursheet->cury,cursheet->curz,clocked));
226         memmove(buf,mbspos(buf,1),strlen(mbspos(buf,1))+1);
227         s=buf;
228       }
229       else if (isalpha(c))
230       {
231         buf[0] = '"';
232         buf[1] = c;
233         buf[2] = 0;
234         s=buf+2;
235       }
236       else
237       {
238         if (c < 256) buf[0]=c;
239         else buf[0] = 0;
240         buf[1]='\0';
241         s=buf+1;
242       }
243       do
244       {
245         int r;
246 
247         x=mbslen(buf)-mbslen(s);
248         if ((r=line_edit(cursheet,buf,sizeof(buf),clocked ? _("Clocked cell contents:") : _("Cell contents:"),&x,&offx))<0) return r;
249         s=buf;
250         if (buf[0] == '"' && buf[strlen(buf)-1] != '"' && strlen(buf)+1 < sizeof(buf)) {
251             buf[strlen(buf)+1] = 0;
252             buf[strlen(buf)] = '"';
253         }
254         t=scan(&s);
255       } while (*s!='\0' && t==(Token**)0);
256     }
257     if (t!=(Token**)0 && *t==(Token*)0) { free(t); t=(Token**)0; }
258     moveto(cursheet,curx,cury,curz);
259     putcont(cursheet,cursheet->curx,cursheet->cury,cursheet->curz,t,clocked);
260     forceupdate(cursheet);
261   }
262   return 0;
263 }
264 /*}}}*/
265 /* do_label       -- modify cell label */ /*{{{*/
do_label(Sheet * sheet)266 static int do_label(Sheet *sheet)
267 {
268   /* variables */ /*{{{*/
269   char buf[1024],oldlabel[1024];
270   size_t edx,offx,ok;
271   Token t;
272   int x,y,z,x1,y1,z1,x2,y2,z2;
273   int c;
274   /*}}}*/
275 
276   assert(sheet!=(Sheet*)0);
277   if (sheet->mark1x==-1 && locked(sheet,sheet->curx,sheet->cury,sheet->curz)) line_msg(_("Cell label:"),_("Cell is locked"));
278   else
279   {
280     get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 0);
281     ok=edx=offx=0;
282     (void)strcpy(buf,getlabel(sheet,sheet->curx,sheet->cury,sheet->curz));
283     (void)strcpy(oldlabel,buf);
284     do
285     {
286       if ((c=line_idedit(buf,sizeof(buf),_("Cell label:"),&edx,&offx))<0) return c;
287       if (buf[0]=='\0') ok=1;
288       else
289       {
290         ok=((t=findlabel(sheet,buf)).type==EEK || (t.type==LOCATION && t.u.location[0]==sheet->curx && t.u.location[1]==sheet->cury && t.u.location[2]==sheet->curz));
291         tfree(&t);
292       }
293     } while (!ok);
294     setlabel(sheet,sheet->curx,sheet->cury,sheet->curz,buf,1);
295     if (buf[0]!='\0') for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) relabel(sheet,oldlabel,buf,x,y,z);
296   }
297   return -1;
298 }
299 /*}}}*/
300 /* do_columnwidth -- set the column width */ /*{{{*/
do_columnwidth(Sheet * cursheet)301 static int do_columnwidth(Sheet *cursheet)
302 {
303   /* variables */ /*{{{*/
304   size_t edx,offx;
305   int n;
306   int x,x1,x2,z,z1,z2;
307   int c;
308   /*}}}*/
309 
310   offx=0;
311   edx=0;
312   n=columnwidth(cursheet,cursheet->curx,cursheet->curz);
313   do if ((c=line_numedit(&n,_("Column width:"),&edx,&offx))<0) return c; while (n<=0);
314   if (cursheet->mark1x==-1)
315   /* range is the current cell */ /*{{{*/
316   {
317     x1=x2=cursheet->curx;
318     z1=z2=cursheet->curz;
319   }
320   /*}}}*/
321   else
322   /* range is the marked cube */ /*{{{*/
323   {
324     x1=cursheet->mark1x; x2=cursheet->mark2x;
325     z1=cursheet->mark1z; z2=cursheet->mark2z;
326   }
327   /*}}}*/
328   for (x=x1; x<=x2; ++x) for (z=z1; z<=z2; ++z) setwidth(cursheet,x,z,n);
329   return -1;
330 }
331 /*}}}*/
332 /* do_attribute   -- set cell attributes */ /*{{{*/
do_attribute(Sheet * cursheet,Key action)333 static void do_attribute(Sheet *cursheet, Key action)
334 {
335   /* variables */ /*{{{*/
336   int x,y,z;
337   int x1,y1,z1;
338   int x2,y2,z2;
339   int c = 0;
340   /*}}}*/
341 
342   get_mark(cursheet, &x1, &x2, &y1, &y2, &z1, &z2, 0);
343 
344   if (action != ADJUST_LOCK && cursheet->mark1x==-1 &&  action != ADJUST_LOCK && locked(cursheet,cursheet->curx,cursheet->cury,cursheet->curz))
345   {
346     line_msg(_("Cell attribute:"),_("Cell is locked"));
347     return;
348   }
349   switch ((int)action)
350   {
351     /* 0       -- adjust left */ /*{{{*/
352     case ADJUST_LEFT:
353     {
354       if (cursheet->mark1x != -1 && line_ok(_("Make block left-adjusted:"), 0) <= 0) break;
355       for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) setadjust(cursheet,x,y,z,LEFT);
356       break;
357     }
358     /*}}}*/
359     /* 1       -- adjust right */ /*{{{*/
360     case ADJUST_RIGHT:
361     {
362       if (cursheet->mark1x != -1 && line_ok(_("Make block right-adjusted:"), 0) <= 0) break;
363       for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) setadjust(cursheet,x,y,z,RIGHT);
364       break;
365     }
366     /*}}}*/
367     /* 2       -- adjust centered */ /*{{{*/
368     case ADJUST_CENTER:
369     {
370       if (cursheet->mark1x != -1 && line_ok(_("Make block centered:"), 0) <= 0) break;
371       for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) setadjust(cursheet,x,y,z,CENTER);
372       break;
373     }
374     /*}}}*/
375     /* 3       -- set scientific notation flag */ /*{{{*/
376     case ADJUST_SCIENTIFIC:
377     {
378       int n;
379 
380       if (cursheet->mark1x==-1) n = !getscientific(cursheet,x1,y1,z1);
381       else n = line_ok(_("Make block notation scientific:"), getscientific(cursheet,x1,y1,z1));
382       if (n >= 0) for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) setscientific(cursheet,x,y,z,n);
383       break;
384     }
385     /*}}}*/
386     /* 5       -- set precision */ /*{{{*/
387     case ADJUST_PRECISION:
388     {
389       size_t ex,offx;
390       int n;
391 
392       offx=0;
393       ex=0;
394       n=getprecision(cursheet,x1,y1,z1);
395       do if (line_numedit(&n,cursheet->mark1x==-1 ? _("Precision for cell:") : _("Precision for block:"),&ex,&offx)==-1) return; while (n!=-1 && (n==0 || n>20));
396       for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) setprecision(cursheet,x,y,z,n);
397       break;
398     }
399     /*}}}*/
400     /* 6       -- shadow */ /*{{{*/
401     case ADJUST_SHADOW:
402     {
403       int n;
404 
405       if (cursheet->mark1x==-1) n = !shadowed(cursheet,x1,y1,z1);
406       else n = line_ok(_("Shadow block:"), shadowed(cursheet,x1,y1,z1));
407       if (x1 == 0 && n == 1) {
408         line_msg(_("Shadow cell:"),_("You can not shadow cells in column 0"));
409         break;
410       }
411 
412       if (n >= 0) {
413         for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z)
414         {
415           int rx;
416 
417           if (n==0) for (rx=x+1; shadowed(cursheet,rx,y,z); ++rx) shadow(cursheet,rx,y,z,0);
418           else if (x>0) shadow(cursheet,x,y,z,1);
419         }
420       }
421       break;
422     }
423     /*}}}*/
424     /* 7       -- transparent */ /*{{{*/
425     case ADJUST_TRANSPARENT:
426     {
427       int n;
428 
429       if (cursheet->mark1x==-1) n = !transparent(cursheet,x1,y1,z1);
430       else n = line_ok(_("Make block transparent:"), transparent(cursheet,x1,y1,z1));
431       if (n >= 0) for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) maketrans(cursheet,x,y,z,n);
432       break;
433     }
434     /*}}}*/
435     /* 8       -- bold */ /*{{{*/
436     case ADJUST_BOLD:
437     {
438       int n;
439 
440       if (cursheet->mark1x==-1) n = !isbold(cursheet,x1,y1,z1);
441       else n = line_ok(_("Make block bold:"), isbold(cursheet,x1,y1,z1));
442       if (n >= 0) for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) bold(cursheet,x,y,z,n);
443       break;
444     }
445     /*}}}*/
446     /* 9       -- underline */ /*{{{*/
447     case ADJUST_UNDERLINE:
448     {
449       int n;
450 
451       if (cursheet->mark1x==-1) n = !underlined(cursheet,x1,y1,z1);
452       else n = line_ok(_("Make block underlined:"), underlined(cursheet,x1,y1,z1));
453       if (n >= 0) for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) underline(cursheet,x,y,z,n);
454       break;
455     }
456     /*}}}*/
457     /* 1       -- edit label and goto end */ /*{{{*/
458     case ADJUST_LABEL:
459     {
460       do_label(cursheet);
461       return;
462     }
463     /*}}}*/
464     /* 2       -- lock */ /*{{{*/
465     case ADJUST_LOCK:
466     {
467       int n;
468 
469       if (cursheet->mark1x==-1) n = !locked(cursheet,x1,y1,z1);
470       else n = line_ok(_("Lock block:"), locked(cursheet,x1,y1,z1));
471       if (n >= 0) for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) lockcell(cursheet,x,y,z,n);
472 
473       break;
474     }
475     /*}}}*/
476     /* 3       -- ignore */ /*{{{*/
477     case ADJUST_IGNORE:
478     {
479       int n;
480 
481       if (cursheet->mark1x==-1) n = !ignored(cursheet,x1,y1,z1);
482       else n = line_ok(_("Ignore values of all cells in this block:"), ignored(cursheet,x1,y1,z1));
483       if (n >= 0) for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) igncell(cursheet,x,y,z,n);
484       break;
485     }
486     /*}}}*/
487     /* default -- should not happen */ /*{{{*/
488       default: assert(0);
489     /*}}}*/
490   }
491   if (c>=0)
492   {
493     if (cursheet->mark1x==-1) redraw_cell(cursheet, cursheet->curx, cursheet->cury, cursheet->curz);
494     else redraw_sheet(cursheet);
495   }
496   forceupdate(cursheet);
497   return;
498 }
499 /*}}}*/
500 /* do_savexdr     -- save sheet as XDR file */ /*{{{*/
do_savexdr(Sheet * cursheet,const char * name)501 static int do_savexdr(Sheet *cursheet, const char *name)
502 {
503   char buf[PATH_MAX];
504   const char *msg;
505   unsigned int count;
506 
507   if (!name) {
508       name = cursheet->name;
509 
510       if (strcmp(name+strlen(name)-3,".tp")) {
511         snprintf(buf, sizeof(buf), "%s.tp", name);
512         name = buf;
513       }
514   }
515 
516   if ((msg = savexdr(cursheet, name, &count))) {
517     line_msg(_("Save sheet to XDR file:"), msg);
518     return -2;
519   }
520 
521   snprintf(buf, sizeof(buf), _("%u cells written"), count);
522   if (!batch) line_msg(_("Save sheet to XDR file:"),buf);
523   return -1;
524 }
525 /*}}}*/
526 /* do_saveport    -- save sheet as portable ASCII file */ /*{{{*/
do_saveport(Sheet * cursheet,const char * name)527 static int do_saveport(Sheet *cursheet, const char *name)
528 {
529   char buf[PATH_MAX];
530   const char *msg;
531   unsigned int count;
532 
533   if (!name) name = cursheet->name;
534 
535   if ((msg = saveport(cursheet, name, &count))) {
536     line_msg(_("Save sheet to ASCII file:"),msg);
537     return -2;
538   }
539 
540   snprintf(buf, sizeof(buf), _("%u cells written"), count);
541   if (!batch) line_msg(_("Save sheet to ASCII file:"),buf);
542   return -1;
543 }
544 /*}}}*/
545 /* do_savetbl     -- save sheet as tbl file */ /*{{{*/
do_savetbl(Sheet * cursheet,const char * name)546 static int do_savetbl(Sheet *cursheet, const char *name)
547 {
548   char buf[PATH_MAX];
549   const char *msg;
550   int standalone=0;
551   int x1,y1,z1,x2,y2,z2;
552   unsigned int count;
553 
554   get_mark(cursheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
555   if (!name) {
556     name = cursheet->name;
557     if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
558   }
559 
560   if ((msg = savetbl(cursheet, name, !standalone, x1, y1, z1, x2, y2, z2, &count))) {
561     line_msg(_("Save in tbl format to file:"),msg);
562     return -2;
563   }
564 
565   snprintf(buf, sizeof(buf), _("%u cells written"), count);
566   if (!batch) line_msg(_("Save in tbl format to file:"), buf);
567   return -1;
568 }
569 /*}}}*/
570 /* do_savelatex   -- save sheet as LaTeX file */ /*{{{*/
do_savelatex(Sheet * cursheet,const char * name)571 static int do_savelatex(Sheet *cursheet, const char *name)
572 {
573   char buf[PATH_MAX];
574   const char *msg;
575   int standalone=0;
576   int x1,y1,z1,x2,y2,z2;
577   unsigned int count;
578 
579   get_mark(cursheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
580   if (!name) {
581     name = cursheet->name;
582     if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
583   }
584 
585   if ((msg = savelatex(cursheet, name, !standalone, x1, y1, z1, x2, y2, z2, &count))) {
586     line_msg(_("Save in LaTeX format to file:"),msg);
587     return -2;
588   }
589 
590   snprintf(buf, sizeof(buf), _("%u cells written"), count);
591   if (!batch) line_msg(_("Save in LaTeX format to file:"), buf);
592   return -1;
593 }
594 /*}}}*/
595 /* do_savecontext   -- save sheet as ConTeXt file */ /*{{{*/
do_savecontext(Sheet * cursheet,const char * name)596 static int do_savecontext(Sheet *cursheet, const char *name)
597 {
598   char buf[PATH_MAX];
599   const char *msg;
600   int standalone=0;
601   int x1,y1,z1,x2,y2,z2;
602   unsigned int count;
603 
604   get_mark(cursheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
605   if (!name) {
606     name = cursheet->name;
607     if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
608   }
609 
610   if ((msg = savecontext(cursheet, name, !standalone, x1, y1, z1, x2, y2, z2, &count))) {
611     line_msg(_("Save in ConTeXt format to file:"),msg);
612     return -2;
613   }
614 
615   snprintf(buf, sizeof(buf), _("%u cells written"), count);
616   if (!batch) line_msg(_("Save in ConTeXt format to file:"), buf);
617   return -1;
618 }
619 /*}}}*/
620 /* do_savehtml    -- save sheet as HTML file */ /*{{{*/
do_savehtml(Sheet * cursheet,const char * name)621 static int do_savehtml(Sheet *cursheet, const char *name)
622 {
623   char buf[PATH_MAX];
624   const char *msg;
625   int standalone=0;
626   int x1,y1,z1,x2,y2,z2;
627   unsigned int count;
628 
629   get_mark(cursheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
630   if (!name) {
631     name = cursheet->name;
632     if ((standalone=line_ok(_("Save as stand-alone document:"),1))<0) return standalone;
633   }
634 
635   if ((msg = savehtml(cursheet, name, !standalone, x1, y1, z1, x2, y2, z2, &count))) {
636     line_msg(_("Save in HTML format to file:"),msg);
637     return -2;
638   }
639 
640   snprintf(buf, sizeof(buf), _("%u cells written"), count);
641   if (!batch) line_msg(_("Save in HTML format to file:"), buf);
642   return -1;
643 }
644 /*}}}*/
645 /* do_savetext    -- save sheet as formatted text file */ /*{{{*/
do_savetext(Sheet * cursheet,const char * name)646 static int do_savetext(Sheet *cursheet, const char *name)
647 {
648   char buf[PATH_MAX];
649   const char *msg;
650   int x1,y1,z1,x2,y2,z2;
651   unsigned int count;
652 
653   get_mark(cursheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
654   if (!name) name = cursheet->name;
655 
656   if ((msg = savetext(cursheet, name, x1, y1, z1, x2, y2, z2, &count))) {
657     line_msg(_("Save in plain text format to file:"),msg);
658     return -2;
659   }
660 
661   snprintf(buf, sizeof(buf), _("%u cells written"), count);
662   if (!batch) line_msg(_("Save in plain text format to file:"), buf);
663   return -1;
664 }
665 /*}}}*/
666 /* do_savecsv     -- save sheet as CSV file */ /*{{{*/
do_savecsv(Sheet * cursheet,const char * name)667 static int do_savecsv(Sheet *cursheet, const char *name)
668 {
669   char buf[PATH_MAX];
670   const char *msg;
671   int x1,y1,z1,x2,y2,z2;
672   unsigned int count;
673   int sep = 0;
674   const char seps[4] = ",;\t";
675 
676   get_mark(cursheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
677   if (!name) {
678     MenuChoice menu[4];
679     name = cursheet->name;
680 
681     menu[0].str=mystrmalloc(_("cC)omma (,)")); menu[0].c='\0';
682     menu[1].str=mystrmalloc(_("sS)emicolon (;)")); menu[1].c='\0';
683     menu[2].str=mystrmalloc(_("tT)ab (\\t)")); menu[2].c='\0';
684     menu[3].str=(char*)0;
685     sep=line_menu(_("Choose separator:"),menu,0);
686     if (sep < 0) return sep;
687   }
688 
689   if ((msg = savecsv(cursheet, name, seps[sep], x1, y1, z1, x2, y2, z2, &count))) {
690     line_msg(_("Save in CSV format to file:"),msg);
691     return -2;
692   }
693 
694   snprintf(buf, sizeof(buf), _("%u cells written"), count);
695   if (!batch) line_msg(_("Save in CSV format to file:"), buf);
696   return -1;
697 }
698 /*}}}*/
699 /* do_loadxdr     -- load sheet from XDR file */ /*{{{*/
do_loadxdr(Sheet * cursheet)700 static int do_loadxdr(Sheet *cursheet)
701 {
702   const char *msg;
703 
704   if ((msg=loadxdr(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from XDR file:"),msg);
705   return -1;
706 }
707 /*}}}*/
708 /* do_loadport    -- load sheet from portable ASCII file */ /*{{{*/
do_loadport(Sheet * cursheet)709 static int do_loadport(Sheet *cursheet)
710 {
711   const char *msg;
712   /*}}}*/
713 
714   if ((msg=loadport(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from ASCII file:"),msg);
715   return -1;
716 }
717 /*}}}*/
718 /* do_loadsc      -- load sheet from SC file */ /*{{{*/
do_loadsc(Sheet * cursheet)719 static int do_loadsc(Sheet *cursheet)
720 {
721   const char *msg;
722 
723   if ((msg=loadsc(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from SC file:"),msg);
724   return -1;
725 }
726 /*}}}*/
727 /* do_loadwk1     -- load sheet from WK1 file */ /*{{{*/
do_loadwk1(Sheet * cursheet)728 static int do_loadwk1(Sheet *cursheet)
729 {
730   const char *msg;
731 
732   if ((msg=loadwk1(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from WK1 file:"),msg);
733   return -1;
734 }
735 /*}}}*/
736 /* do_loadcsv     -- load/merge sheet from CSV file */ /*{{{*/
do_loadcsv(Sheet * cursheet)737 static int do_loadcsv(Sheet *cursheet)
738 {
739   const char *msg;
740 
741   if ((msg=loadcsv(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from CSV file:"),msg);
742   return -1;
743 }
744 /*}}}*/
745 /* do_mark        -- set mark */ /*{{{*/
do_mark(Sheet * cursheet,int force)746 void do_mark(Sheet *cursheet, int force)
747 {
748   if (force==0)
749   {
750     if (!cursheet->marking && cursheet->mark1x==-1) force=1;
751     else if (cursheet->marking) force=2;
752     else force=3;
753   }
754   switch (force)
755   {
756     case 1:
757     {
758       cursheet->mark1x=cursheet->mark2x=cursheet->curx;
759       cursheet->mark1y=cursheet->mark2y=cursheet->cury;
760       cursheet->mark1z=cursheet->mark2z=cursheet->curz;
761       cursheet->marking=1;
762       break;
763     }
764     case 2:
765     {
766       cursheet->marking=0;
767       break;
768     }
769     case 3:
770     {
771       cursheet->mark1x=-1;
772       break;
773     }
774     default: assert(0);
775   }
776 }
777 /*}}}*/
778 static int do_name(Sheet *cursheet);
779 /* do_save        -- save sheet */ /*{{{*/
do_save(Sheet * cursheet)780 static int do_save(Sheet *cursheet)
781 {
782     const char *ext = cursheet->name;
783     if (ext==(char*)0) return do_name(cursheet);
784 
785     ext += strlen(ext)-1;
786 
787     if (!strcmp(ext-3, ".tpa")) return do_saveport(cursheet, NULL);
788     if (!strcmp(ext-3, ".tbl")) return do_savetbl(cursheet, NULL);
789     if (!strcmp(ext-5, ".latex")) return do_savelatex(cursheet, NULL);
790     if (!strcmp(ext-4, ".html")) return do_savehtml(cursheet, NULL);
791     if (!strcmp(ext-3, ".csv")) return do_savecsv(cursheet, NULL);
792     if (!strcmp(ext-3, ".txt")) return do_savetext(cursheet, NULL);
793     if (!strcmp(ext-3, ".tex")) return do_savecontext(cursheet, NULL);
794     return do_savexdr(cursheet, NULL);
795 }
796 /*}}}*/
797 /* do_name        -- (re)name sheet */ /*{{{*/
do_name(Sheet * cursheet)798 static int do_name(Sheet *cursheet)
799 {
800     const char *name;
801 
802     name = line_file(cursheet->name, _("Teapot \t*.tp\nTeapot ASCII \t*.tpa\ntbl \t*.tbl\nLaTeX \t*.latex\nHTML \t*.html\nCSV \t*.csv\nFormatted ASCII \t*.txt\nConTeXt \t*.tex"), _("New file name:"), 1);
803     if (!name) return -1;
804 
805     if (cursheet->name!=(char*)0) free(cursheet->name);
806     cursheet->name=strdup(name);
807     return do_save(cursheet);
808 }
809 /*}}}*/
810 /* do_load        -- load sheet */ /*{{{*/
do_load(Sheet * cursheet)811 static int do_load(Sheet *cursheet)
812 {
813     const char *name, *ext;
814 
815     if (doanyway(cursheet, _("Sheet modified, load new file anyway?")) != 1) return -1;
816 
817     name = line_file(cursheet->name, _("Teapot \t*.tp\nTeapot ASCII \t*.tpa\nSC Spreadsheet Calculator \t*.sc\nLotus 1-2-3 \t*.wk1\nCSV \t*.csv"), _("Load sheet:"), 0);
818     if (!name) return -1;
819     if (cursheet->name!=(char*)0) free(cursheet->name);
820     cursheet->name=strdup(name);
821 
822     ext = name+strlen(name)-1;
823     if (!strcmp(ext-3, ".tpa")) return do_loadport(cursheet);
824     if (!strcmp(ext-2, ".sc")) return do_loadsc(cursheet);
825     if (!strcmp(ext-3, ".wk1")) return do_loadwk1(cursheet);
826     if (!strcmp(ext-3, ".csv")) return do_loadcsv(cursheet);
827     return do_loadxdr(cursheet);
828 }
829 /*}}}*/
830 
831 /* do_clear       -- clear block */ /*{{{*/
do_clear(Sheet * sheet)832 static int do_clear(Sheet *sheet)
833 {
834   /* variables */ /*{{{*/
835   int x,y,z;
836   int x1,y1,z1;
837   int x2,y2,z2;
838   int c;
839   /*}}}*/
840 
841   if (sheet->mark1x==-1 && locked(sheet,sheet->curx,sheet->cury,sheet->curz)) line_msg(_("Clear cell:"),_("Cell is locked"));
842   else
843   {
844     if (sheet->mark1x!=-1)
845     {
846       if ((c=line_ok(_("Clear block:"),0))<0) return c;
847       else if (c!=1) return -1;
848     }
849     get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 0);
850     for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) freecell(sheet,x,y,z);
851     cachelabels(sheet);
852     forceupdate(sheet);
853   }
854   return -1;
855 }
856 /*}}}*/
857 /* do_insert      -- insert block */ /*{{{*/
do_insert(Sheet * sheet)858 static int do_insert(Sheet *sheet)
859 {
860   /* variables */ /*{{{*/
861   int x1,y1,z1,x2,y2,z2,reply;
862   /*}}}*/
863 
864   /* ask for direction of insertation */ /*{{{*/
865   {
866     MenuChoice menu[4];
867 
868     menu[0].str=mystrmalloc(_("cC)olumn")); menu[0].c='\0';
869     menu[1].str=mystrmalloc(_("rR)ow")); menu[1].c='\0';
870     menu[2].str=mystrmalloc(_("dD)epth")); menu[2].c='\0';
871     menu[3].str=(char*)0;
872     reply=line_menu(_("Insert:"),menu,0);
873     free(menu[0].str);
874     free(menu[1].str);
875     free(menu[2].str);
876     if (reply<0) return reply;
877   }
878   /*}}}*/
879   if (sheet->mark1x==-1)
880   /* ask if current cell or whole dimension should be used */ /*{{{*/
881   {
882     /* variables */ /*{{{*/
883     MenuChoice menu[3];
884     int r;
885     /*}}}*/
886 
887     /* show menu */ /*{{{*/
888     switch (reply)
889     {
890       case 0: menu[0].str=mystrmalloc(_("wW)hole column")); break;
891       case 1: menu[0].str=mystrmalloc(_("wW)hole line")); break;
892       case 2: menu[0].str=mystrmalloc(_("wW)hole sheet")); break;
893       default: assert(0);
894     }
895     menu[1].str=mystrmalloc(_("sS)ingle cell")); menu[1].c='\0';
896     menu[2].str=(char*)0;
897     r=line_menu(_("Insert:"),menu,0);
898     free(menu[0].str);
899     free(menu[1].str);
900     /*}}}*/
901     switch (r)
902     {
903       /*  0 -- use whole dimension */ /*{{{*/
904       case 0:
905       {
906         switch (reply)
907         {
908           /*  0 -- use whole column */ /*{{{*/
909           case 0:
910           {
911             x1=x2=sheet->curx;
912             y1=0; y2=sheet->dimy;
913             z1=z2=sheet->curz;
914             break;
915           }
916           /*}}}*/
917           /*  1 -- use whole line */ /*{{{*/
918           case 1:
919           {
920             x1=0; x2=sheet->dimx;
921             y1=y2=sheet->cury;
922             z1=z2=sheet->curz;
923             break;
924           }
925           /*}}}*/
926           /*  2 -- use whole layer */ /*{{{*/
927           case 2:
928           {
929             x1=0; x2=sheet->dimx;
930             y1=0; y2=sheet->dimy;
931             z1=z2=sheet->curz;
932             break;
933           }
934           /*}}}*/
935           /*  default -- should not happen */ /*{{{*/
936           default: assert(0);
937           /*}}}*/
938         }
939         break;
940       }
941       /*}}}*/
942       /*  1 -- use current cell */ /*{{{*/
943       case 1:
944       {
945         x1=x2=sheet->curx;
946         y1=y2=sheet->cury;
947         z1=z2=sheet->curz;
948         break;
949       }
950       /*}}}*/
951       /* -2,-1 -- go up or abort */ /*{{{*/
952       case -2:
953       case -1: return r;
954       /*}}}*/
955       /* default -- should not happen */ /*{{{*/
956       default: assert(0);
957       /*}}}*/
958     }
959   }
960   /*}}}*/
961   else
962   /* range is the marked cube */ /*{{{*/
963   {
964       get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 0);
965   }
966   /*}}}*/
967   switch (reply)
968   {
969     /*  0      -- columns */ /*{{{*/
970     case 0: insertcube(sheet,x1,y1,z1,x2,y2,z2,IN_X); break;
971     /*}}}*/
972     /*  1      -- rows */ /*{{{*/
973     case 1: insertcube(sheet,x1,y1,z1,x2,y2,z2,IN_Y); break;
974     /*}}}*/
975     /*  2      -- depth */ /*{{{*/
976     case 2: insertcube(sheet,x1,y1,z1,x2,y2,z2,IN_Z); break;
977     /*}}}*/
978   }
979   return 0;
980 }
981 /*}}}*/
982 /* do_delete      -- delete block */ /*{{{*/
do_delete(Sheet * sheet)983 static int do_delete(Sheet *sheet)
984 {
985   /* variables */ /*{{{*/
986   int x1,y1,z1,x2,y2,z2,reply;
987   /*}}}*/
988 
989   firstmenu:
990   /* ask for direction of deletion */ /*{{{*/
991   {
992     MenuChoice menu[4];
993 
994     menu[0].str=mystrmalloc(_("cC)olumn")); menu[0].c='\0';
995     menu[1].str=mystrmalloc(_("rR)ow")); menu[1].c='\0';
996     menu[2].str=mystrmalloc(_("dD)epth")); menu[2].c='\0';
997     menu[3].str=(char*)0;
998     reply=line_menu(_("Delete:"),menu,0);
999     free(menu[0].str);
1000     free(menu[1].str);
1001     free(menu[2].str);
1002     if (reply<0) return reply;
1003   }
1004   /*}}}*/
1005   if (sheet->mark1x==-1)
1006   /* ask if range is the current cell or whole dimension should be used */ /*{{{*/
1007   {
1008     /* variables */ /*{{{*/
1009     MenuChoice menu[3];
1010     int r;
1011     /*}}}*/
1012 
1013     /* show menu */ /*{{{*/
1014     switch (reply)
1015     {
1016       case 0: menu[0].str=mystrmalloc(_("wW)hole column")); break;
1017       case 1: menu[0].str=mystrmalloc(_("wW)hole line")); break;
1018       case 2: menu[0].str=mystrmalloc(_("wW)hole sheet")); break;
1019       default: assert(0);
1020     }
1021     menu[1].str=mystrmalloc(_("sS)ingle cell")); menu[1].c='\0';
1022     menu[2].str=(char*)0;
1023     r=line_menu(_("Delete:"),menu,0);
1024     free(menu[0].str);
1025     free(menu[1].str);
1026     /*}}}*/
1027     switch (r)
1028     {
1029       /*  0 -- use whole dimension */ /*{{{*/
1030       case 0:
1031       {
1032         switch (reply)
1033         {
1034           /*  0 -- use whole column */ /*{{{*/
1035           case 0:
1036           {
1037             x1=x2=sheet->curx;
1038             y1=0; y2=sheet->dimy;
1039             z1=z2=sheet->curz;
1040             break;
1041           }
1042           /*}}}*/
1043           /*  1 -- use whole line */ /*{{{*/
1044           case 1:
1045           {
1046             x1=0; x2=sheet->dimx;
1047             y1=y2=sheet->cury;
1048             z1=z2=sheet->curz;
1049             break;
1050           }
1051           /*}}}*/
1052           /*  2 -- use whole layer */ /*{{{*/
1053           case 2:
1054           {
1055             x1=0; x2=sheet->dimx;
1056             y1=0; y2=sheet->dimy;
1057             z1=z2=sheet->curz;
1058             break;
1059           }
1060           /*}}}*/
1061           /*  default -- should not happen */ /*{{{*/
1062           default: assert(0);
1063           /*}}}*/
1064         }
1065         break;
1066       }
1067       /*}}}*/
1068       /*  1 -- use current cell */ /*{{{*/
1069       case 1:
1070       {
1071         x1=x2=sheet->curx;
1072         y1=y2=sheet->cury;
1073         z1=z2=sheet->curz;
1074         break;
1075       }
1076       /*}}}*/
1077       /* -1 -- abort */ /*{{{*/
1078       case -1: return -1;
1079       /*}}}*/
1080       /* -2 -- go back to previous menu */ /*{{{*/
1081       case -2: goto firstmenu;
1082       /*}}}*/
1083       /* default -- should not happen */ /*{{{*/
1084       default: assert(0);
1085       /*}}}*/
1086     }
1087   }
1088   /*}}}*/
1089   else
1090   /* range is the marked cube */ /*{{{*/
1091   {
1092       get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 0);
1093   }
1094   /*}}}*/
1095   switch(reply)
1096   {
1097     /*  0      -- columns */ /*{{{*/
1098     case 0: deletecube(sheet,x1,y1,z1,x2,y2,z2,IN_X); break;
1099     /*}}}*/
1100     /*  1      -- rows */ /*{{{*/
1101     case 1: deletecube(sheet,x1,y1,z1,x2,y2,z2,IN_Y); break;
1102     /*}}}*/
1103     /*  2      -- depth */ /*{{{*/
1104     case 2: deletecube(sheet,x1,y1,z1,x2,y2,z2,IN_Z); break;
1105     /*}}}*/
1106   }
1107   return -1;
1108 }
1109 /*}}}*/
1110 /* do_move        -- copy or move a block */ /*{{{*/
do_move(Sheet * sheet,int copy,int force)1111 static int do_move(Sheet *sheet, int copy, int force)
1112 {
1113   int c;
1114 
1115   c=-1;
1116   if (sheet->mark1x==-1) line_msg(copy ? _("Copy block:") : _("Move block:"),_("No block marked"));
1117   else if (force || (c=line_ok(copy ? _("Copy block:") : _("Move block:"),0))==1)
1118   {
1119     int x1,y1,z1;
1120     int x2,y2,z2;
1121 
1122     get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 0);
1123     moveblock(sheet,x1,y1,z1,x2,y2,z2,sheet->curx,sheet->cury,sheet->curz,copy);
1124     if (!copy) sheet->mark1x=-1;
1125   }
1126   if (c<0) return c; else return -1;
1127 }
1128 /*}}}*/
1129 /* do_fill        -- fill a block */ /*{{{*/
do_fill(Sheet * sheet)1130 static int do_fill(Sheet *sheet)
1131 {
1132   /* variables */ /*{{{*/
1133   size_t offx,edx;
1134   int cols,rows,layers;
1135   int x,y,z;
1136   int x1,y1,z1;
1137   int x2,y2,z2;
1138   int c;
1139   /*}}}*/
1140 
1141   if (sheet->mark1x==-1) line_msg(_("Fill block:"),_("No block marked"));
1142   else
1143   {
1144     get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 0);
1145     cols=rows=layers=1;
1146     firstmenu:
1147     offx=0;
1148     edx=0;
1149     do if ((c=line_numedit(&cols,_("Number of column-wise repetitions:"),&edx,&offx))<0) return c; while (cols<=0);
1150     secondmenu:
1151     offx=0;
1152     edx=0;
1153     do
1154     {
1155       c=line_numedit(&rows,_("Number of row-wise repetitions:"),&edx,&offx);
1156       if (c==-1) return -1;
1157       else if (c==-2) goto firstmenu;
1158     } while (rows<=0);
1159     offx=0;
1160     edx=0;
1161     do
1162     {
1163       c=line_numedit(&layers,_("Number of depth-wise repetitions:"),&edx,&offx);
1164       if (c==-1) return -1;
1165       else if (c==-2) goto secondmenu;
1166     } while (layers<=0);
1167     for (x=0; x<cols; ++x) for (y=0; y<rows; ++y) for (z=0; z<layers; ++z) moveblock(sheet,x1,y1,z1,x2,y2,z2,sheet->curx+x*(x2-x1+1),sheet->cury+y*(y2-y1+1),sheet->curz+z*(z2-z1+1),1);
1168   }
1169   return -1;
1170 }
1171 /*}}}*/
1172 /* do_sort        -- sort block */ /*{{{*/
do_sort(Sheet * sheet)1173 static int do_sort(Sheet *sheet)
1174 {
1175   /* variables */ /*{{{*/
1176   MenuChoice menu1[4],menu2[3],menu3[3];
1177   Sortkey sk[MAX_SORTKEYS];
1178   unsigned int key;
1179   size_t x,offx;
1180   const char *msg;
1181   Direction in_dir=(Direction)-2; /* cause run time error */
1182   int x1,y1,z1,x2,y2,z2;
1183   int doit=-1;
1184   int c;
1185   int last;
1186   /*}}}*/
1187 
1188   /* note and order block coordinates */ /*{{{*/
1189   get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
1190   /*}}}*/
1191   /* build menues */ /*{{{*/
1192   menu1[0].str=mystrmalloc(_("cC)olumn"));     menu1[0].c='\0';
1193   menu1[1].str=mystrmalloc(_("rR)ow"));     menu1[1].c='\0';
1194   menu1[2].str=mystrmalloc(_("dD)epth"));     menu1[2].c='\0';
1195   menu1[3].str=(char*)0;
1196   menu2[0].str=mystrmalloc(_("sS)ort region"));  menu2[0].c='\0';
1197   menu2[1].str=mystrmalloc(_("aA)dd key"));  menu2[1].c='\0';
1198   menu2[2].str=(char*)0;
1199   menu3[0].str=mystrmalloc(_("aA)scending"));  menu3[0].c='\0';
1200   menu3[1].str=mystrmalloc(_("dD)escending")); menu3[0].c='\0';
1201   menu3[2].str=(char*)0;
1202   /*}}}*/
1203 
1204   last=-1;
1205   /* ask for sort direction */ /*{{{*/
1206   zero: switch (c=line_menu(_("Sort block:"),menu1,0))
1207   {
1208     /*  0      -- in X */ /*{{{*/
1209     case 0: in_dir=IN_X; break;
1210     /*}}}*/
1211     /*  1      -- in Y */ /*{{{*/
1212     case 1: in_dir=IN_Y; break;
1213     /*}}}*/
1214     /*  2      -- in Z */ /*{{{*/
1215     case 2: in_dir=IN_Z; break;
1216     /*}}}*/
1217     /* -2,-1   -- abort */ /*{{{*/
1218     case -2:
1219     case -1: goto greak;
1220     /*}}}*/
1221     /* default -- should not happen */ /*{{{*/
1222     default: assert(0);
1223     /*}}}*/
1224   }
1225   last=0;
1226   /*}}}*/
1227   key=0;
1228   do
1229   {
1230     /* ask for positions */ /*{{{*/
1231     one: if (in_dir==IN_X) sk[key].x=0; else /* ask for x position */ /*{{{*/
1232     {
1233       x=0;
1234       offx=0;
1235       sk[key].x=0;
1236       do
1237       {
1238         c=line_numedit(&(sk[key].x),_("X position of key vector:"),&x,&offx);
1239         if (c==-1) goto greak;
1240         else if (c==-2) switch (last)
1241         {
1242           case -1: goto greak;
1243           case 0: goto zero;
1244           case 2: goto two;
1245           case 3: goto three;
1246           case 5: goto five;
1247         }
1248       } while (sk[key].x<0);
1249       last=1;
1250     }
1251     /*}}}*/
1252     two: if (in_dir==IN_Y) sk[key].y=0; else /* ask for y position */ /*{{{*/
1253     {
1254       x=0;
1255       offx=0;
1256       sk[key].y=0;
1257       do
1258       {
1259         c=line_numedit(&(sk[key].y),_("Y position of key vector:"),&x,&offx);
1260         if (c==-1) goto greak;
1261         else if (c==-2) switch (last)
1262         {
1263           case -1: goto greak;
1264           case 0: goto zero;
1265           case 1: goto one;
1266           case 3: goto three;
1267           case 5: goto five;
1268           default: assert(0);
1269         }
1270       } while (sk[key].y<0);
1271       last=2;
1272     }
1273     /*}}}*/
1274     three: if (in_dir==IN_Z) sk[key].z=0; else /* ask for z position */ /*{{{*/
1275     {
1276       x=0;
1277       offx=0;
1278       sk[key].z=0;
1279       do
1280       {
1281         c=line_numedit(&(sk[key].z),_("Z position of key vector:"),&x,&offx);
1282         if (c==-1) goto greak;
1283         else if (c==-2) switch (last)
1284         {
1285           case -1: goto greak;
1286           case 0: goto zero;
1287           case 1: goto one;
1288           case 2: goto two;
1289           case 5: goto five;
1290           default: assert(0);
1291         }
1292       } while (sk[key].z<0);
1293       last=3;
1294     }
1295     /*}}}*/
1296     /*}}}*/
1297     /* ask for sort key */ /*{{{*/
1298     four: sk[key].sortkey=0;
1299     switch (c=line_menu(_("Sort block:"),menu3,0))
1300     {
1301       /*  0      -- ascending */ /*{{{*/
1302       case 0: sk[key].sortkey|=ASCENDING; break;
1303       /*}}}*/
1304       /*  1      -- descending */ /*{{{*/
1305       case 1: sk[key].sortkey&=~ASCENDING; break;
1306       /*}}}*/
1307       /* -1      -- abort */ /*{{{*/
1308       case -1: goto greak;
1309       /*}}}*/
1310       /* -2      -- go to first menu */ /*{{{*/
1311       case -2: switch (last)
1312       {
1313         case -1: goto greak;
1314         case 1: goto one;
1315         case 2: goto two;
1316         case 3: goto three;
1317         default: assert(0);
1318       }
1319       /*}}}*/
1320       /* default -- should not happen */ /*{{{*/
1321       default: assert(0);
1322       /*}}}*/
1323     }
1324     last=4;
1325     /*}}}*/
1326     ++key;
1327     five:
1328     if (key==MAX_SORTKEYS) /* ask for sort comfirmation */ /*{{{*/
1329     {
1330       c=line_ok(_("Sort block:"),0);
1331       if (c==-1) goto greak;
1332       else if (c==-2) goto four;
1333       else if (c==0) doit=1;
1334     }
1335     /*}}}*/
1336     else /* ask for sort or adding another key */ /*{{{*/
1337     switch (line_menu(_("Sort block:"),menu2,0))
1338     {
1339       /*       0 -- sort it */ /*{{{*/
1340       case 0: doit=1; break;
1341       /*}}}*/
1342       /*       1 -- add another key */ /*{{{*/
1343       case 1: doit=0; break;
1344       /*}}}*/
1345       /*      -1 -- abort */ /*{{{*/
1346       case -1: goto greak;
1347       /*}}}*/
1348       case -2: goto four;
1349       /* default -- should not happen */ /*{{{*/
1350       default: assert(0);
1351       /*}}}*/
1352     }
1353     /*}}}*/
1354     last=5;
1355   } while (!doit);
1356   c=-1;
1357   if ((msg=sortblock(sheet,x1,y1,z1,x2,y2,z2,in_dir,sk,key))!=(const char*)0) line_msg(_("Sort block:"),msg);
1358   greak:
1359   /* free menues */ /*{{{*/
1360   free((char*)menu1[0].str);
1361   free((char*)menu1[1].str);
1362   free((char*)menu1[2].str);
1363   free((char*)menu2[0].str);
1364   free((char*)menu2[1].str);
1365   free((char*)menu2[2].str);
1366   free((char*)menu3[0].str);
1367   free((char*)menu3[1].str);
1368   free((char*)menu3[2].str);
1369   /*}}}*/
1370   return c;
1371 }
1372 /*}}}*/
1373 /* do_batchsort -- sort block in a batch*/ /*{{{*/
do_batchsort(Sheet * sheet,Direction dir,char * arg)1374 static void do_batchsort(Sheet *sheet, Direction dir, char* arg)
1375 {
1376   Sortkey sk[MAX_SORTKEYS];
1377   int x1,y1,z1,x2,y2,z2;
1378   unsigned int key = 0;
1379   char* next;
1380   while( *arg != '\0' )
1381   {
1382     while (isspace((int)*arg)) arg++;
1383     sk[key].x=sk[key].y=sk[key].z=sk[key].sortkey=0;
1384     switch (*arg)
1385     {
1386       case 'a': sk[key].sortkey|=ASCENDING; arg++; break;
1387       case 'd': sk[key].sortkey&=~ASCENDING; arg++; break;
1388     }
1389     if ( *arg != '\0' && dir != IN_X ) { sk[key].x=strtol(arg, &next, 10); arg = next; }
1390     if ( *arg != '\0' && dir != IN_Y ) { sk[key].y=strtol(arg, &next, 10); arg = next; }
1391     if ( *arg != '\0' && dir != IN_Z ) { sk[key].z=strtol(arg, &next, 10); arg = next; }
1392     key++;
1393   }
1394   get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
1395   sortblock(sheet, x1, y1, z1, x2, y2, z2, dir, sk, key);
1396 }
1397 /*}}}*/
1398 /* do_mirror      -- mirror block */ /*{{{*/
do_mirror(Sheet * sheet)1399 static int do_mirror(Sheet *sheet)
1400 {
1401   /* variables */ /*{{{*/
1402   int x1,y1,z1,x2,y2,z2,reply;
1403   /*}}}*/
1404 
1405   /* note and order block coordinates */ /*{{{*/
1406   get_mark(sheet, &x1, &x2, &y1, &y2, &z1, &z2, 1);
1407   /*}}}*/
1408   /* ask for direction of mirroring */ /*{{{*/
1409   {
1410     MenuChoice menu[4];
1411 
1412     menu[0].str=mystrmalloc(_("lL)eft-right")); menu[0].c='\0';
1413     menu[1].str=mystrmalloc(_("uU)pside-down")); menu[1].c='\0';
1414     menu[2].str=mystrmalloc(_("fF)ront-back")); menu[2].c='\0';
1415     menu[3].str=(char*)0;
1416     reply=line_menu(_("Mirror block:"),menu,0);
1417     free(menu[0].str);
1418     free(menu[1].str);
1419     free(menu[2].str);
1420     if (reply<0) return reply;
1421   }
1422   /*}}}*/
1423   switch (reply)
1424   {
1425     /*  0      -- left-right */ /*{{{*/
1426     case 0: mirrorblock(sheet,x1,y1,z1,x2,y2,z2,IN_X); break;
1427     /*}}}*/
1428     /*  1      -- upside-down */ /*{{{*/
1429     case 1: mirrorblock(sheet,x1,y1,z1,x2,y2,z2,IN_Y); break;
1430     /*}}}*/
1431     /*  2      -- front-back */ /*{{{*/
1432     case 2: mirrorblock(sheet,x1,y1,z1,x2,y2,z2,IN_Z); break;
1433     /*}}}*/
1434     default: assert(0);
1435   }
1436   return 0;
1437 }
1438 /*}}}*/
1439 /* do_goto        -- go to a specific cell */ /*{{{*/
do_goto(Sheet * sheet,const char * expr)1440 static int do_goto(Sheet *sheet, const char *expr)
1441 {
1442   /* variables */ /*{{{*/
1443   char buf[1024];
1444   const char *s;
1445   size_t x,offx;
1446   Token **t;
1447   int c;
1448   /*}}}*/
1449 
1450   assert(sheet!=(Sheet*)0);
1451   buf[0]='\0';
1452   x=offx=0;
1453   if (expr) strcpy(buf,expr);
1454   else if ((c=line_edit(sheet,buf,sizeof(buf),_("Go to location:"),&x,&offx))<0) return c;
1455   s=buf;
1456   t=scan(&s);
1457   if (t!=(Token**)0)
1458   {
1459     Token value;
1460 
1461     upd_x=sheet->curx;
1462     upd_y=sheet->cury;
1463     upd_z=sheet->curz;
1464     upd_sheet=sheet;
1465     value=eval(t);
1466     tvecfree(t);
1467     if (value.type==LOCATION && value.u.location[0]>=0 && value.u.location[1]>=0 && value.u.location[2]>=0)
1468         moveto(sheet,value.u.location[0],value.u.location[1],value.u.location[2]);
1469     else
1470         line_msg(_("Go to location:"),_("Not a valid location"));
1471     tfree(&value);
1472   }
1473   return -1;
1474 }
1475 /*}}}*/
1476 
1477 /* do_sheetcmd    -- process one key press */ /*{{{*/
do_sheetcmd(Sheet * cursheet,Key c,int moveonly)1478 int do_sheetcmd(Sheet *cursheet, Key c, int moveonly)
1479 {
1480   switch ((int)c)
1481   {
1482     case K_GOTO: do_goto(cursheet, (const char *)0); break;
1483     case K_COLWIDTH: do_columnwidth(cursheet); break;
1484     case BLOCK_CLEAR: do_clear(cursheet); redraw_sheet(cursheet); break;
1485     case BLOCK_INSERT: do_insert(cursheet); redraw_sheet(cursheet); break;
1486     case BLOCK_DELETE: do_delete(cursheet); redraw_sheet(cursheet); break;
1487     case BLOCK_MOVE: do_move(cursheet,0,0); redraw_sheet(cursheet); break;
1488     case BLOCK_COPY: do_move(cursheet,1,0); redraw_sheet(cursheet); break;
1489     case BLOCK_FILL: do_fill(cursheet); redraw_sheet(cursheet); break;
1490     case BLOCK_SORT: do_sort(cursheet); redraw_sheet(cursheet); break;
1491     case BLOCK_MIRROR: do_mirror(cursheet); redraw_sheet(cursheet); break;
1492     case ADJUST_LEFT:
1493     case ADJUST_RIGHT:
1494     case ADJUST_CENTER:
1495     case ADJUST_SCIENTIFIC:
1496     case ADJUST_PRECISION:
1497     case ADJUST_SHADOW:
1498     case ADJUST_BOLD:
1499     case ADJUST_UNDERLINE:
1500     case ADJUST_TRANSPARENT:
1501     case ADJUST_LABEL:
1502     case ADJUST_LOCK:
1503     case ADJUST_IGNORE: do_attribute(cursheet, c); break;
1504     /* UP         -- move up */ /*{{{*/
1505     case K_UP:
1506     {
1507       relmoveto(cursheet, 0, -1, 0);
1508       break;
1509     }
1510     /*}}}*/
1511     /* DOWN       -- move down */ /*{{{*/
1512     case K_DOWN:
1513     {
1514       relmoveto(cursheet, 0, 1, 0);
1515       break;
1516     }
1517     /*}}}*/
1518     /* LEFT       -- move left */ /*{{{*/
1519     case K_LEFT:
1520     {
1521       relmoveto(cursheet, -1, 0, 0);
1522       break;
1523     }
1524     /*}}}*/
1525     /* RIGHT      -- move right */ /*{{{*/
1526     case K_RIGHT:
1527     {
1528       relmoveto(cursheet, 1, 0, 0);
1529       break;
1530     }
1531     /*}}}*/
1532     /* FIRSTL     -- move to first line */ /*{{{*/
1533     case K_FIRSTL:
1534     case '<':
1535     {
1536       moveto(cursheet, -1, 0, -1);
1537       break;
1538     }
1539     /*}}}*/
1540     /* LASTL      -- move to last line */ /*{{{*/
1541     case K_LASTL:
1542     case '>':
1543     {
1544       moveto(cursheet, -1, (cursheet->dimy ? cursheet->dimy-1 : 0), -1);
1545       break;
1546     }
1547     /*}}}*/
1548     /* HOME       -- move to beginning of line */ /*{{{*/
1549     case K_HOME:
1550     {
1551       moveto(cursheet, 0, -1, -1);
1552       break;
1553     }
1554     /*}}}*/
1555     /* END        -- move to end of line */ /*{{{*/
1556     case K_END:
1557     {
1558       moveto(cursheet, (cursheet->dimx ? cursheet->dimx-1 : 0), -1, -1);
1559       break;
1560     }
1561     /*}}}*/
1562     /* +          -- move one sheet down */ /*{{{*/
1563     case K_NSHEET:
1564     case '+':
1565     {
1566       relmoveto(cursheet, 0, 0, 1);
1567       break;
1568     }
1569     /*}}}*/
1570     /* -          -- move one sheet up */ /*{{{*/
1571     case K_PSHEET:
1572     case '-':
1573     {
1574       relmoveto(cursheet, 0, 0, -1);
1575       break;
1576     }
1577     /*}}}*/
1578     /* *          -- move to bottom sheet */ /*{{{*/
1579     case K_LSHEET:
1580     case '*':
1581     {
1582       moveto(cursheet, -1, -1, (cursheet->dimz ? cursheet->dimz-1 : 0));
1583       break;
1584     }
1585     /*}}}*/
1586     /* _          -- move to top sheet */ /*{{{*/
1587     case K_FSHEET:
1588     case '_':
1589     {
1590       moveto(cursheet, -1, -1, 0);
1591       break;
1592     }
1593     /*}}}*/
1594     /* ENTER      -- edit current cell */ /*{{{*/
1595     case K_ENTER: if (moveonly) break; do_edit(cursheet,'\0',(const char*)0,0); break;
1596     /*}}}*/
1597     /* MENTER     -- edit current clocked cell */ /*{{{*/
1598     case K_MENTER: if (moveonly) break; do_edit(cursheet,'\0',(const char*)0,1); break;
1599     /*}}}*/
1600     /* ", @, digit -- edit current cell with character already in buffer */ /*{{{*/
1601     case K_BACKSPACE:
1602     case K_DC:
1603     case '"':
1604     case '@':
1605     case '0':
1606     case '1':
1607     case '2':
1608     case '3':
1609     case '4':
1610     case '5':
1611     case '6':
1612     case '7':
1613     case '8':
1614     case '9': if (moveonly) break; do_edit(cursheet,c,(const char*)0,0); break;
1615     /*}}}*/
1616     /* MARK       -- toggle block marking */ /*{{{*/
1617     case '.':
1618     case K_MARK: if (moveonly) break; do_mark(cursheet,0); break;
1619     /*}}}*/
1620     /* _("Save sheet file format:")   -- save menu */ /*{{{*/
1621     case K_SAVEMENU: if (moveonly) break; do_save(cursheet); break;
1622     /*}}}*/
1623     /* _("Load sheet file format:")   -- load menu */ /*{{{*/
1624     case K_LOAD:
1625     case K_LOADMENU: if (moveonly) break; do_load(cursheet); break;
1626     /*}}}*/
1627     /* _("nN)ame")       -- set name */ /*{{{*/
1628     case K_NAME: if (moveonly) break; do_name(cursheet); break;
1629     /*}}}*/
1630 #ifdef ENABLE_HELP
1631     case K_HELP: show_text(helpfile); break;
1632 #else
1633     case K_HELP: show_text(_("Sorry, manual is not installed.")); break;
1634 #endif
1635     case K_ABOUT: show_text(_("<html><head><title>About teapot</title></head><body><center><pre>\n\n"
1636         "               ` ',`    '  '                   \n"
1637         "                `   '  ` ' '                   \n"
1638         "                 `' '   '`'                    \n"
1639         "                 ' `   ' '`                    \n"
1640         "    '           '` ' ` '`` `                   \n"
1641         "    `.   Table Editor And Planner, or:         \n"
1642         "      ,         . ,   ,  . .                   \n"
1643         "                ` '   `  ' '                   \n"
1644         "     `::\\    /:::::::::::::::::\\   ___         \n"
1645         "      `::\\  /:::::::::::::::::::\\,'::::\\       \n"
1646         "       :::\\/:::::::::::::::::::::\\/   \\:\\      \n"
1647         "       :::::::::::::::::::::::::::\\    :::     \n"
1648         "       ::::::::::::::::::::::::::::;  /:;'     \n"
1649         "       `::::::::::::::::::::::::::::_/:;'      \n"
1650         "         `::::::::::::::::::::::::::::'        \n"
1651         "          `////////////////////////'           \n"
1652         "           `:::::::::::::::::::::'             \n"
1653         "</pre>\n"
1654         "<p>Teapot " VERSION "</p>\n"
1655         "\n"
1656         "<p>Original Version: Michael Haardt<br>\n"
1657         "Current Maintainer: Joerg Walter<br>\n"
1658         "Home Page: <a href='http://www.syntax-k.de/projekte/teapot/'>http://www.syntax-k.de/projekte/teapot/</a></p>\n"
1659         "\n"
1660         "<p>Copyright 1995-2006 Michael Haardt,<br>\n"
1661         "Copyright 2009-2010 Joerg Walter (<a href='mailto:info@syntax-k.de'>info@syntax-k.de</a>)</p></center>\n"
1662         "\f"
1663         "<p>This program is free software: you can redistribute it and/or modify\n"
1664         "it under the terms of the GNU General Public License as published by\n"
1665         "the Free Software Foundation, either version 3 of the License, or\n"
1666         "(at your option) any later version.</p>\n"
1667         "\n"
1668         "<p>This program is distributed in the hope that it will be useful,\n"
1669         "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1670         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1671         "GNU General Public License for more details.</p>\n"
1672         "\n"
1673         "<p>You should have received a copy of the GNU General Public License\n"
1674         "along with this program.  If not, see <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.</p>"
1675         "</body></html>"));
1676         break;
1677     /* MENU, / -- main menu */ /*{{{*/
1678     case '/': if (!moveonly && do_sheetcmd(cursheet, show_menu(cursheet), 0)) return 1; break;
1679     /*}}}*/
1680     /* _("sS)ave")       -- save in current native format */ /*{{{*/
1681     case K_SAVE: do_save(cursheet); break;
1682     /*}}}*/
1683     /* _("cC)opy")       -- copy block */ /*{{{*/
1684     case K_COPY: if (moveonly) break; do_move(cursheet,1,1); break;
1685     /*}}}*/
1686     /* RECALC     -- recalculate */ /*{{{*/
1687     case K_RECALC: if (moveonly) break; forceupdate(cursheet); break;
1688     /*}}}*/
1689     /* _("Usage: clock(condition,location[,location])")      -- clock */ /*{{{*/
1690     case K_CLOCK:
1691     {
1692       int x,y,z;
1693 
1694       for (x=0; x<cursheet->dimx; ++x)
1695       for (y=0; y<cursheet->dimy; ++y)
1696       for (z=0; z<cursheet->dimz; ++z)
1697       clk(cursheet,x,y,z);
1698       update(cursheet);
1699       break;
1700     }
1701     /*}}}*/
1702     /* NPAGE      -- page down    */ /*{{{*/
1703     case K_NPAGE:
1704     {
1705       cursheet->offy+=(cursheet->maxy-3);
1706       relmoveto(cursheet, 0, cursheet->maxy-3, 0);
1707       break;
1708     }
1709     /*}}}*/
1710     /* PPAGE      -- page up    */ /*{{{*/
1711     case K_PPAGE:
1712     {
1713       cursheet->offy = (cursheet->offy>=(cursheet->maxy-3) ? cursheet->offy-(cursheet->maxy-3) : 0);
1714       relmoveto(cursheet, 0, (cursheet->cury>=(cursheet->maxy-3) ? -(cursheet->maxy-3) : -cursheet->cury), 0);
1715       break;
1716     }
1717     /*}}}*/
1718     /* FPAGE      -- page right    */ /*{{{*/
1719     case K_FPAGE:
1720     {
1721       cursheet->offx+=cursheet->width;
1722       relmoveto(cursheet, cursheet->width, 0, 0);
1723       break;
1724     }
1725     /*}}}*/
1726     /* BPAGE      -- page left */ /*{{{*/
1727     case K_BPAGE:
1728     {
1729       cursheet->offx=(cursheet->offx>=cursheet->width ? cursheet->offx-cursheet->width : 0);
1730       relmoveto(cursheet, (cursheet->curx>=cursheet->width ? -cursheet->width : -cursheet->curx), 0, 0);
1731       break;
1732     }
1733     /*}}}*/
1734     /* SAVEQUIT   -- save and quit */ /*{{{*/
1735     case K_SAVEQUIT:
1736     {
1737       if (moveonly) break;
1738       if (do_save(cursheet)!=-2) return 1;
1739       break;
1740     }
1741     /*}}}*/
1742     /* _("qQ)uit")       -- quit */ /*{{{*/
1743     case K_QUIT: if (moveonly) break; return 1;
1744     /*}}}*/
1745     default:
1746         if (isalpha(c) && !moveonly) do_edit(cursheet,c,(const char*)0,0);
1747         break;
1748   }
1749   return 0;
1750 }
1751 /*}}}*/
1752 
1753 /* main */ /*{{{*/
main(int argc,char * argv[])1754 int main(int argc, char *argv[])
1755 {
1756   /* variables */ /*{{{*/
1757   Sheet sheet,*cursheet;
1758   int o;
1759   const char *loadfile;
1760   int always_redraw=0;
1761   char ln[1024];
1762   /*}}}*/
1763 
1764   setlocale(LC_ALL, "");
1765   find_helpfile(helpfile, sizeof(helpfile), argv[0]);
1766 
1767   /* parse options */ /*{{{*/
1768   while ((o=getopt(argc,argv,"abhnrqHp:?"))!=EOF) switch (o)
1769   {
1770     /* a       -- use ascii as default */ /*{{{*/
1771     case 'a':
1772     {
1773       usexdr=0;
1774       break;
1775     }
1776     /*}}}*/
1777     /* b       -- run batch */ /*{{{*/
1778     case 'b': batch=1; break;
1779     /*}}}*/
1780     /* n       -- no quoted strings */ /*{{{*/
1781     case 'n': quote=0; break;
1782     /*}}}*/
1783     /* q       -- force quoted strings */ /*{{{*/
1784     case 'q': quote=1; break;
1785     /*}}}*/
1786     /* H       -- no row/column headers */ /*{{{*/
1787     case 'H': header=0; break;
1788     /*}}}*/
1789     /* r       -- always redraw */ /*{{{*/
1790     case 'r':
1791     {
1792       always_redraw=1;
1793       break;
1794     }
1795     /*}}}*/
1796     /* p       -- precision */ /*{{{*/
1797     case 'p':
1798     {
1799       long n;
1800       char *end;
1801 
1802       n=strtol(optarg,&end,0);
1803       if (*end || n<0 || n>DBL_DIG)
1804       {
1805         fprintf(stderr,_("teapot: precision must be between 0 and %d.\n"),DBL_DIG);
1806         exit(1);
1807       }
1808       def_precision=n;
1809       break;
1810     }
1811     /*}}}*/
1812     /* default -- includes ? and h */ /*{{{*/
1813     default:
1814     {
1815       fprintf(stderr,_(
1816           "Usage: %s [-a] [-b] [-n] [-H] [-r] [-p digits] [file]\n"
1817           "       -a: use ASCII file format as default\n"
1818           "       -b: batch mode\n"
1819           "       -q: display strings in quotes\n"
1820           "       -H: hide row/column headers\n"
1821           "       -r: redraw more often\n"
1822           "       -p: set decimal precision\n"
1823           ), argv[0]);
1824       exit(1);
1825     }
1826     /*}}}*/
1827   }
1828   loadfile=(optind<argc ? argv[optind] : (const char*)0);
1829   /*}}}*/
1830   /* create empty sheet */ /*{{{*/
1831   cursheet=&sheet;
1832   cursheet->curx=cursheet->cury=cursheet->curz=0;
1833   cursheet->offx=cursheet->offy=0;
1834   cursheet->dimx=cursheet->dimy=cursheet->dimz=0;
1835   cursheet->sheet=(Cell**)0;
1836   cursheet->column=(int*)0;
1837   cursheet->orix=0;
1838   cursheet->oriy=0;
1839   cursheet->maxx=0;
1840   cursheet->maxy=0;
1841   cursheet->name=(char*)0;
1842   cursheet->mark1x=-1;
1843   cursheet->marking=0;
1844   cursheet->changed=0;
1845   cursheet->moveonly=0;
1846   cursheet->clk=0;
1847   (void)memset(cursheet->labelcache,0,sizeof(cursheet->labelcache));
1848   /*}}}*/
1849   /* start display */ /*{{{*/
1850   if (!batch) {
1851     display_init(&sheet, always_redraw);
1852     line_msg((const char*)0,"");
1853   }
1854   /*}}}*/
1855   if (loadfile) /* load given sheet */ /*{{{*/
1856   {
1857     const char *msg;
1858 
1859     cursheet->name=mystrmalloc(loadfile);
1860     if (usexdr)
1861     {
1862       if ((msg=loadxdr(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from XDR file:"),msg);
1863     }
1864     else
1865     {
1866       if ((msg=loadport(cursheet,cursheet->name))!=(const char*)0) line_msg(_("Load sheet from ASCII file:"),msg);
1867     }
1868   }
1869   /*}}}*/
1870   if (batch) /* process batch */ /*{{{*/
1871   while (fgets(ln,sizeof(ln),stdin)!=(char*)0)
1872   {
1873     /* variables */ /*{{{*/
1874     size_t len;
1875     char *cmd,*arg;
1876     /*}}}*/
1877 
1878     /* set cmd and arg */ /*{{{*/
1879     ++batchln;
1880     len=strlen(ln);
1881     if (len && ln[len-1]=='\n') ln[len-1]='\0';
1882     cmd=ln; while (isspace((int)*cmd)) ++cmd;
1883     arg=cmd;
1884     while (*arg && !isspace((int)*arg)) ++arg;
1885     while (isspace((int)*arg)) *arg++='\0';
1886     /*}}}*/
1887 
1888     /* goto location */ /*{{{*/
1889     if (strcmp(cmd,"goto")==0) do_goto(cursheet,arg);
1890     /*}}}*/
1891     /* from location */ /*{{{*/
1892     else if (strcmp(cmd,"from")==0)
1893     {
1894       do_goto(cursheet,arg);
1895       do_mark(cursheet,1);
1896     }
1897     /*}}}*/
1898     /* to location */ /*{{{*/
1899     else if (strcmp(cmd,"to")==0)
1900     {
1901       do_goto(cursheet,arg);
1902       do_mark(cursheet,2);
1903     }
1904     /*}}}*/
1905     /* save-tbl file  */ /*{{{*/
1906     else if (strcmp(cmd,"save-tbl")==0) do_savetbl(cursheet,arg);
1907     /*}}}*/
1908     /* save-latex file  */ /*{{{*/
1909     else if (strcmp(cmd,"save-latex")==0) do_savelatex(cursheet,arg);
1910     /*}}}*/
1911     /* save-context file  */ /*{{{*/
1912     else if (strcmp(cmd,"save-context")==0) do_savecontext(cursheet,arg);
1913     /*}}}*/
1914     /* save-csv file  */ /*{{{*/
1915     else if (strcmp(cmd,"save-csv")==0) do_savecsv(cursheet,arg);
1916     /*}}}*/
1917     /* save-html file  */ /*{{{*/
1918     else if (strcmp(cmd,"save-html")==0) do_savehtml(cursheet,arg);
1919     /*}}}*/
1920     /* load-csv file  */ /*{{{*/
1921     else if (strcmp(cmd,"load-csv")==0) { loadcsv(cursheet,arg); forceupdate(cursheet); }
1922     /*}}}*/
1923     /* sort in x direction */ /*{{{*/
1924     else if (strcmp(cmd,"sort-x")==0) do_batchsort(cursheet, IN_X, arg);
1925     /*}}}*/
1926     /* sort in y direction */ /*{{{*/
1927     else if (strcmp(cmd,"sort-y")==0) do_batchsort(cursheet, IN_Y, arg);
1928     /*}}}*/
1929     /* sort in z direction */ /*{{{*/
1930     else if (strcmp(cmd,"sort-z")==0) do_batchsort(cursheet, IN_Z, arg);
1931     /*}}}*/
1932     /* this is an unknown command */ /*{{{*/
1933     else line_msg(_("Unknown batch command:"),cmd);
1934     /*}}}*/
1935   }
1936   /*}}}*/
1937   else /* process interactive input */ /*{{{*/
1938   {
1939     display_main(cursheet);
1940     display_end();
1941   }
1942   /*}}}*/
1943   freesheet(cursheet,1);
1944   fclose(stdin);
1945   return 0;
1946 }
1947 /*}}}*/
1948