1 #ifndef NO_POSIX_SOURCE
2 #undef _POSIX_SOURCE
3 #define _POSIX_SOURCE 1
4 #undef _POSIX_C_SOURCE
5 #define _POSIX_C_SOURCE 2
6 #endif
7
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <assert.h>
11 #include <ctype.h>
12 #include <limits.h>
13 #ifdef ENABLE_UTF8
14 #include <ncursesw/curses.h>
15 #else
16 #include <curses.h>
17 #endif
18 #include <errno.h>
19 #include <pwd.h>
20 #include <termios.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #ifdef NEED_BCOPY
27 #define memmove(dst,src,len) bcopy(src,dst,len)
28 #endif
29 #ifdef OLD_REALLOC
30 #define realloc(s,l) myrealloc(s,l)
31 #endif
32
33 #ifdef DMALLOC
34 #include "dmalloc.h"
35 #endif
36
37
38 #include "complete.h"
39 #include "default.h"
40 #include "display.h"
41 #include "eval.h"
42 #include "main.h"
43 #include "misc.h"
44 #include "sheet.h"
45 #include "utf8.h"
46
47 static Key wgetc(void);
48
49 /* redraw -- redraw whole screen */
redraw(void)50 static void redraw(void)
51 {
52 (void)touchwin(curscr);
53 (void)wrefresh(curscr);
54 }
55
56
57 /* do_attribute -- set cell attributes */
do_attribute(Sheet * cursheet)58 static int do_attribute(Sheet *cursheet)
59 {
60
61 MenuChoice mainmenu[5];
62 MenuChoice adjmenu[11];
63 int c;
64
65 /* create menus */
66 adjmenu[0].str=mystrmalloc(_("lL)eft")); adjmenu[0].c='\0';
67 adjmenu[1].str=mystrmalloc(_("rR)ight")); adjmenu[1].c='\0';
68 adjmenu[2].str=mystrmalloc(_("cC)entered")); adjmenu[2].c='\0';
69 adjmenu[3].str=mystrmalloc(_("11).23e1 <-> 12.3")); adjmenu[3].c='\0';
70 adjmenu[4].str=mystrmalloc(_("pP)recision")); adjmenu[4].c='\0';
71 adjmenu[5].str=mystrmalloc(_("sS)hadow")); adjmenu[5].c='\0';
72 adjmenu[6].str=mystrmalloc(_("bB)old")); adjmenu[6].c='\0';
73 adjmenu[7].str=mystrmalloc(_("uU)nderline")); adjmenu[7].c='\0';
74 adjmenu[8].str=mystrmalloc(_("oO)utput special characters")); adjmenu[8].c='\0';
75 adjmenu[9].str=(char*)0;
76
77 mainmenu[0].str=mystrmalloc(_("rR)epresentation")); mainmenu[0].c='\0';
78 mainmenu[1].str=mystrmalloc(_("lL)abel")); mainmenu[1].c='\0';
79 mainmenu[2].str=mystrmalloc(_("oLo)ck")); mainmenu[2].c='\0';
80 mainmenu[3].str=mystrmalloc(_("iI)gnore")); mainmenu[3].c='\0';
81 mainmenu[4].str=(char*)0;
82
83 do
84 {
85 c = line_menu(cursheet->mark1x==-1 ? _("Cell attribute:") : _("Block attribute:"),mainmenu,0);
86 if (cursheet->mark1x==-1 && c!=2 && locked(cursheet,cursheet->curx,cursheet->cury,cursheet->curz)) line_msg(_("Cell attribute:"),_("Cell is locked"));
87 else
88 {
89 switch (c)
90 {
91 case -2:
92 case -1: c = KEY_CANCEL; break;
93 case 0:
94 {
95 switch (c=line_menu(cursheet->mark1x==-1 ? _("Cell attribute:") : _("Block attribute:"),adjmenu,0))
96 {
97 case -2:
98 case -1: c = K_INVALID; break;
99 case 0: c = ADJUST_LEFT; break;
100 case 1: c = ADJUST_RIGHT; break;
101 case 2: c = ADJUST_CENTER; break;
102 case 3: c = ADJUST_SCIENTIFIC; break;
103 case 4: c = ADJUST_PRECISION; break;
104 case 5: c = ADJUST_SHADOW; break;
105 case 6: c = ADJUST_BOLD; break;
106 case 7: c = ADJUST_UNDERLINE; break;
107 case 8: c = ADJUST_TRANSPARENT; break;
108 default: assert(0);
109 }
110 break;
111 }
112 case 1: c = ADJUST_LABEL; break;
113 case 2: c = ADJUST_LOCK; break;
114 case 3: c = ADJUST_IGNORE; break;
115 default: assert(0);
116 }
117 }
118 } while (c == K_INVALID);
119 if (c == KEY_CANCEL) c = K_INVALID;
120
121 /* free menus */
122 free(mainmenu[0].str);
123 free(mainmenu[1].str);
124 free(mainmenu[2].str);
125 free(mainmenu[3].str);
126 free(adjmenu[0].str);
127 free(adjmenu[1].str);
128 free(adjmenu[2].str);
129 free(adjmenu[3].str);
130 free(adjmenu[4].str);
131 free(adjmenu[5].str);
132 free(adjmenu[6].str);
133 free(adjmenu[7].str);
134 free(adjmenu[8].str);
135
136 return c;
137 }
138 /* do_file -- file menu */
do_file(Sheet * cursheet)139 static int do_file(Sheet *cursheet)
140 {
141
142 MenuChoice menu[4];
143 int c;
144
145
146 menu[0].str=mystrmalloc(_("lL)oad")); menu[0].c='\0';
147 menu[1].str=mystrmalloc(_("sS)ave")); menu[1].c='\0';
148 menu[2].str=mystrmalloc(_("nN)ame")); menu[2].c='\0';
149 menu[3].str=(char*)0;
150 c=0;
151 do
152 {
153 switch (c=line_menu(_("File:"),menu,0))
154 {
155 case -2:
156 case -1: c = KEY_CANCEL; break;
157 case 0: c = K_LOADMENU; break;
158 case 1: c = K_SAVEMENU; break;
159 case 2: c = K_NAME; break;
160 default: assert(0);
161 }
162 } while (c == K_INVALID);
163 if (c == KEY_CANCEL) c = K_INVALID;
164 free(menu[0].str);
165 free(menu[1].str);
166 free(menu[2].str);
167 return c;
168 }
169
170
171 /* do_shell -- spawn a shell */
do_shell(void)172 static int do_shell(void)
173 {
174 pid_t pid;
175 struct sigaction interrupt;
176
177 refresh();
178 interrupt.sa_flags=0;
179 sigemptyset(&interrupt.sa_mask);
180 interrupt.sa_handler=SIG_IGN;
181 sigaction(SIGINT,&interrupt,(struct sigaction *)0);
182 sigaction(SIGQUIT,&interrupt,(struct sigaction *)0);
183 switch (pid=fork())
184 {
185 /* -1 */
186 case -1: line_msg(_("Spawn sub shell"),strerror(errno)); break;
187
188 /* 0 */
189 case 0:
190 {
191 const char *shell;
192
193 if ((shell=getenv("SHELL"))==(const char*)0)
194 {
195 struct passwd *pwd;
196
197 if ((pwd=getpwuid(getuid()))==(struct passwd*)0)
198 {
199 shell="/bin/sh";
200 }
201 else
202 {
203 shell=pwd->pw_shell;
204 }
205 }
206 line_msg((const char*)0,_("Sub shell started"));
207 move(LINES-1,0);
208 curs_set(1);
209 refresh();
210 reset_shell_mode();
211 puts("\n");
212 interrupt.sa_handler=SIG_DFL;
213 sigaction(SIGINT,&interrupt,(struct sigaction *)0);
214 sigaction(SIGQUIT,&interrupt,(struct sigaction *)0);
215 execl(shell,shell,(const char*)0);
216 exit(127);
217 break;
218 }
219
220 /* default */
221 default:
222 {
223 pid_t r;
224 int status;
225
226 while ((r=wait(&status))!=-1 && r!=pid);
227 reset_prog_mode();
228 interrupt.sa_handler=SIG_DFL;
229 sigaction(SIGINT,&interrupt,(struct sigaction *)0);
230 sigaction(SIGQUIT,&interrupt,(struct sigaction *)0);
231 clear();
232 refresh();
233 curs_set(0);
234 redraw();
235 }
236
237 }
238 return -1;
239 }
240
241
242 /* do_block -- block menu */
do_block(Sheet * cursheet)243 static int do_block(Sheet *cursheet)
244 {
245 MenuChoice block[9];
246 int c;
247
248 block[0].str=mystrmalloc(_("ecle)ar")); block[0].c='\0';
249 block[1].str=mystrmalloc(_("iI)nsert")); block[1].c='\0';
250 block[2].str=mystrmalloc(_("dD)elete")); block[2].c='\0';
251 block[3].str=mystrmalloc(_("mM)ove")); block[3].c='\0';
252 block[4].str=mystrmalloc(_("cC)opy")); block[4].c='\0';
253 block[5].str=mystrmalloc(_("fF)ill")); block[5].c='\0';
254 block[6].str=mystrmalloc(_("sS)ort")); block[6].c='\0';
255 block[7].str=mystrmalloc(_("rMir)ror")); block[7].c='\0';
256 block[8].str=(char*)0;
257 c=0;
258 do
259 {
260 switch (c=line_menu(_("Block menu:"),block,0))
261 {
262 case -2:
263 case -1: c = KEY_CANCEL; break;
264 case 0: c = BLOCK_CLEAR; break;
265 case 1: c = BLOCK_INSERT; break;
266 case 2: c = BLOCK_DELETE; break;
267 case 3: c = BLOCK_MOVE; break;
268 case 4: c = BLOCK_COPY; break;
269 case 5: c = BLOCK_FILL; break;
270 case 6: c = BLOCK_SORT; break;
271 case 7: c = BLOCK_MIRROR; break;
272 }
273 } while (c == K_INVALID);
274 if (c == KEY_CANCEL) c = K_INVALID;
275 free(block[0].str);
276 free(block[1].str);
277 free(block[2].str);
278 free(block[3].str);
279 free(block[4].str);
280 free(block[5].str);
281 free(block[6].str);
282 free(block[7].str);
283 return c;
284 }
285
286
show_menu(Sheet * cursheet)287 int show_menu(Sheet *cursheet)
288 {
289
290 MenuChoice menu[9];
291 int c = K_INVALID;
292
293
294 menu[0].str=mystrmalloc(_("aA)ttributes")); menu[0].c='\0';
295 menu[1].str=mystrmalloc(_("wW)idth")); menu[1].c='\0';
296 menu[2].str=mystrmalloc(_("bB)lock")); menu[2].c='\0';
297 menu[3].str=mystrmalloc(_("fF)ile")); menu[3].c='\0';
298 menu[4].str=mystrmalloc(_("gG)oto")); menu[4].c='\0';
299 menu[5].str=mystrmalloc(_("sS)hell")); menu[5].c='\0';
300 menu[6].str=mystrmalloc(_("vV)ersion")); menu[6].c='\0';
301 menu[7].str=mystrmalloc(_("qQ)uit")); menu[7].c='\0';
302 menu[8].str=(char*)0;
303
304 do
305 {
306 switch (c=line_menu(_("Main menu:"),menu,0))
307 {
308 case -2:
309 case -1: c = KEY_CANCEL; break;
310 case 0: c = do_attribute(cursheet); break;
311 case 1: c = K_COLWIDTH; break;
312 case 2: c = do_block(cursheet); break;
313 case 3: c = do_file(cursheet); break;
314 case 4: c = K_GOTO; break;
315 case 5: do_shell(); c = KEY_CANCEL; break;
316 case 6: c = K_ABOUT; break;
317 case 7: c = K_QUIT; break;
318 default: assert(0);
319 }
320 } while (c == K_INVALID);
321 if (c == KEY_CANCEL) c = K_INVALID;
322
323 free(menu[0].str);
324 free(menu[1].str);
325 free(menu[2].str);
326 free(menu[3].str);
327 free(menu[4].str);
328 free(menu[5].str);
329 free(menu[6].str);
330 free(menu[7].str);
331
332 return c;
333 }
334
335 /* do_bg -- background teapot */
do_bg(void)336 static void do_bg(void)
337 {
338 struct termios t;
339
340 if (tcgetattr(0,&t)==0 && t.c_cc[VSUSP]!=_POSIX_VDISABLE)
341 {
342 line_msg((const char*)0,_("Teapot stopped"));
343 move(LINES-1,0);
344 curs_set(1);
345 refresh();
346 reset_shell_mode();
347 puts("\n");
348 kill(getpid(),SIGSTOP);
349 clear();
350 refresh();
351 reset_prog_mode();
352 curs_set(0);
353 }
354 else line_msg((const char*)0,_("The susp character is undefined"));
355 }
356
357
358
display_main(Sheet * cursheet)359 void display_main(Sheet *cursheet)
360 {
361 Key k;
362 int quit = 0;
363
364 cursheet->maxx=COLS;
365 cursheet->maxy=LINES-1;
366
367 do
368 {
369 quit = 0;
370 redraw_sheet(cursheet);
371 k=wgetc();
372 wmove(stdscr,LINES-1,0);
373 wclrtoeol(stdscr);
374 switch ((int)k)
375 {
376 case KEY_SUSPEND:
377 case '\032': do_bg(); k = K_INVALID; break;
378 case '\014': redraw(); k = K_INVALID; break;
379 case KEY_F(0):
380 case KEY_F(10): k = show_menu(cursheet); break;
381 }
382 } while (k == K_INVALID || !do_sheetcmd(cursheet,k,0) || doanyway(cursheet,_("Sheet modified, leave anyway?"))!=1);
383 }
384
display_init(Sheet * cursheet,int always_redraw)385 void display_init(Sheet *cursheet, int always_redraw)
386 {
387 initscr();
388 curs_set(0);
389 noecho();
390 raw();
391 nonl();
392 keypad(stdscr,TRUE);
393 clear();
394 refresh();
395 #ifdef HAVE_TYPEAHEAD
396 if (always_redraw) typeahead(-1);
397 #endif
398 }
399
display_end(void)400 void display_end(void)
401 {
402 curs_set(1);
403 echo();
404 noraw();
405 refresh();
406 endwin();
407 }
408
redraw_cell(Sheet * sheet,int x,int y,int z)409 void redraw_cell(Sheet *sheet, int x, int y, int z)
410 {
411 redraw_sheet(sheet);
412 }
413
414 /* redraw_sheet -- draw a sheet with cell cursor */
redraw_sheet(Sheet * sheet)415 void redraw_sheet(Sheet *sheet)
416 {
417
418 int width,col,x,y,again;
419 char pbuf[80];
420 char *buf=malloc(128);
421 size_t bufsz=128;
422 const char *label;
423 char *err;
424 char moveonly;
425
426 assert(sheet!=(Sheet*)0);
427 assert(sheet->curx>=0);
428 assert(sheet->cury>=0);
429 assert(sheet->curz>=0);
430 assert(sheet->offx>=0);
431 assert(sheet->offy>=0);
432
433 /* correct offsets to keep cursor visible */
434 while (shadowed(sheet,sheet->curx,sheet->cury,sheet->curz))
435 {
436 --(sheet->curx);
437 assert(sheet->curx>=0);
438 }
439 if (sheet->cury-sheet->offy>(sheet->maxy-2-header)) sheet->offy=sheet->cury-sheet->maxy+2+header;
440 if (sheet->cury<sheet->offy) sheet->offy=sheet->cury;
441 if (sheet->curx<sheet->offx) sheet->offx=sheet->curx;
442 do
443 {
444 again=0;
445 for (width=4*header,x=sheet->offx,col=0; width<=sheet->maxx; width+=columnwidth(sheet,x,sheet->curz),++x,++col);
446 --col;
447 sheet->width=col;
448 if (sheet->curx!=sheet->offx)
449 {
450 if (col==0) { ++sheet->offx; again=1; }
451 else if (sheet->curx-sheet->offx>=col) { ++sheet->offx; again=1; }
452 }
453 } while (again);
454
455 if (header) {
456 (void)wattron(stdscr,DEF_NUMBER);
457
458 /* draw x numbers */
459 for (width=4; width<sheet->maxx; ++width) mvwaddch(stdscr,0+sheet->oriy,sheet->orix+width,(chtype)(unsigned char)' ');
460 for (width=4,x=sheet->offx; width<sheet->maxx; width+=col,++x)
461 {
462 col=columnwidth(sheet,x,sheet->curz);
463 if (bufsz<(size_t)(col*UTF8SZ+1)) buf=realloc(buf,bufsz=(size_t)(col*UTF8SZ+1));
464 snprintf(buf,bufsz,"%d",x);
465 if (mbslen(buf)>col) {
466 buf[col-1]='$';
467 buf[col]='\0';
468 }
469 adjust(CENTER,buf,(size_t)col);
470 assert(sheet->maxx>=width);
471 if ((sheet->maxx-width)<col) buf[sheet->maxx-width]='\0';
472 mvwaddstr(stdscr,sheet->oriy,sheet->orix+width,buf);
473 }
474
475 /* draw y numbers */
476 for (y=1; y<(sheet->maxy-1); ++y) (void)mvwprintw(stdscr,sheet->oriy+y,sheet->orix,"%-4d",y-1+sheet->offy);
477
478 (void)wattroff(stdscr,DEF_NUMBER);
479
480 /* draw z number */
481 (void)mvwprintw(stdscr,sheet->oriy,sheet->orix,"%3d",sheet->curz);
482 }
483
484 /* draw elements */
485 for (y=header; y<sheet->maxy-1; ++y) for (width=4*header,x=sheet->offx; width<sheet->maxx; width+=columnwidth(sheet,x,sheet->curz),++x)
486 {
487 size_t size,realsize,fill,cutoff;
488 int realx;
489
490 realx=x;
491 cutoff=0;
492 if (x==sheet->offx) while (shadowed(sheet,realx,y-header+sheet->offy,sheet->curz))
493 {
494 --realx;
495 cutoff+=columnwidth(sheet,realx,sheet->curz);
496 }
497 if ((size=cellwidth(sheet,realx,y-header+sheet->offy,sheet->curz)))
498 {
499 int invert;
500
501 if (bufsz<(size*UTF8SZ+1)) buf=realloc(buf,bufsz=(size*UTF8SZ+1));
502 printvalue(buf,(size*UTF8SZ+1),size,quote,getscientific(sheet,realx,y-header+sheet->offy,sheet->curz),getprecision(sheet,realx,y-header+sheet->offy,sheet->curz),sheet,realx,y-header+sheet->offy,sheet->curz);
503 adjust(getadjust(sheet,realx,y-header+sheet->offy,sheet->curz),buf,size);
504 assert(size>=cutoff);
505 if (width+((int)(size-cutoff))>=sheet->maxx)
506 {
507 *(buf+cutoff+sheet->maxx-width)='\0';
508 realsize=sheet->maxx-width+cutoff;
509 }
510 else realsize=size;
511 invert=
512 (
513 (sheet->mark1x!=-1) &&
514 ((x>=sheet->mark1x && x<=sheet->mark2x) || (x>=sheet->mark2x && x<=sheet->mark1x)) &&
515 ((y-header+sheet->offy>=sheet->mark1y && y-header+sheet->offy<=sheet->mark2y) || (y-header+sheet->offy>=sheet->mark2y && y-header+sheet->offy<=sheet->mark1y)) &&
516 ((sheet->curz>=sheet->mark1z && sheet->curz<=sheet->mark2z) || (sheet->curz>=sheet->mark2z && sheet->curz<=sheet->mark1z))
517 );
518 if (x==sheet->curx && (y-header+sheet->offy)==sheet->cury) invert=(sheet->marking ? 1 : 1-invert);
519 if (invert) (void)wattron(stdscr,DEF_CELLCURSOR);
520 if (isbold(sheet,realx,y-header+sheet->offy,sheet->curz)) wattron(stdscr,A_BOLD);
521 if (underlined(sheet,realx,y-header+sheet->offy,sheet->curz)) wattron(stdscr,A_UNDERLINE);
522 (void)mvwaddstr(stdscr,sheet->oriy+y,sheet->orix+width,buf+cutoff);
523 for (fill=mbslen(buf+cutoff); fill<realsize; ++fill) (void)waddch(stdscr,(chtype)(unsigned char)' ');
524 wstandend(stdscr);
525 }
526 }
527
528 /* draw contents of current element */
529 if (bufsz<(unsigned int)(sheet->maxx*UTF8SZ+1)) buf=realloc(buf,bufsz=(sheet->maxx*UTF8SZ+1));
530 label=getlabel(sheet,sheet->curx,sheet->cury,sheet->curz);
531 assert(label!=(const char*)0);
532 moveonly=sheet->moveonly ? *_("V") : *_("E");
533 if (*label=='\0') sprintf(pbuf,"%c @(%d,%d,%d)=",moveonly,sheet->curx,sheet->cury,sheet->curz);
534 else sprintf(pbuf,"%c @(%s)=",moveonly,label);
535 (void)strncpy(buf,pbuf,bufsz);
536 buf[bufsz-1] = 0;
537 if ((err=geterror(sheet,sheet->curx,sheet->cury,sheet->curz))!=(const char*)0)
538 {
539 (void)strncpy(buf, err, bufsz);
540 free(err);
541 }
542 else
543 {
544 print(buf+strlen(buf),bufsz-strlen(buf),0,1,getscientific(sheet,sheet->curx,sheet->cury,sheet->curz),-1,getcont(sheet,sheet->curx,sheet->cury,sheet->curz,0));
545 if (getcont(sheet,sheet->curx,sheet->cury,sheet->curz,1) && mbslen(buf) < (size_t)(sheet->maxx+1-4))
546 {
547 strcat(buf," -> ");
548 print(buf+strlen(buf),bufsz-strlen(buf),0,1,getscientific(sheet,sheet->curx,sheet->cury,sheet->curz),-1,getcont(sheet,sheet->curx,sheet->cury,sheet->curz,1));
549 }
550 }
551 *mbspos(buf, sheet->maxx) = 0;
552 (void)mvwaddstr(stdscr,sheet->oriy+sheet->maxy-1,sheet->orix,buf);
553 for (col=mbslen(buf); col<sheet->maxx; ++col) (void)waddch(stdscr,' ');
554 }
555
556
557 /* line_file -- line editor function for file name entry */
line_file(const char * file,const char * pattern,const char * title,int create)558 const char *line_file(const char *file, const char *pattern, const char *title, int create)
559 {
560 static char buf[PATH_MAX] = "";
561 int rc;
562 size_t dummy1 = 0, dummy2 = 0;
563
564 if (file) strncpy(buf, file, sizeof(buf));
565 buf[sizeof(buf)-1] = 0;
566 rc = line_edit((Sheet*)0, buf, sizeof(buf), title, &dummy1, &dummy2);
567 if (rc < 0) return NULL;
568 return buf;
569 }
570
571
572 /* line_edit -- line editor function */
line_edit(Sheet * sheet,char * buf,size_t size,const char * prompt,size_t * x,size_t * offx)573 int line_edit(Sheet *sheet, char *buf, size_t size, const char *prompt, size_t *x, size_t *offx)
574 {
575 size_t promptlen;
576 char *src, *dest;
577 int i,mx,my,insert;
578 chtype c;
579
580 assert(buf!=(char*)0);
581 assert(prompt!=(char*)0);
582 assert(x!=(size_t*)0);
583 assert(offx!=(size_t*)0);
584
585 (void)curs_set(1);
586 mx=COLS;
587 my=LINES;
588 promptlen=mbslen(prompt)+1;
589 (void)mvwaddstr(stdscr,LINES-1,0,prompt); (void)waddch(stdscr,(chtype)(unsigned char)' ');
590 insert=1;
591
592 do {
593 /* correct offx to cursor stays visible */
594 if (*x<*offx) *offx=*x;
595 if ((*x-*offx)>(mx-promptlen-1)) *offx=*x-mx+promptlen+1;
596
597 /* display buffer */
598 (void)wmove(stdscr,LINES-1,(int)promptlen);
599 src = mbspos(buf, *offx);
600 dest = mbspos(buf, *offx+COLS-promptlen);
601 for (; *src && src < dest; src++) (void)waddch(stdscr,(chtype)(unsigned char)(*src));
602 if (i!=mx) (void)wclrtoeol(stdscr);
603
604 /* show cursor */
605 (void)wmove(stdscr,LINES-1,(int)(*x-*offx+promptlen));
606
607 src = dest = mbspos(buf, *x);
608 c=wgetc();
609 if (sheet!=(Sheet*)0 && sheet->moveonly) switch (c) {
610 /* ^o -- switch back to line editor */
611 case '\t':
612 case '\017': sheet->moveonly=0; break;
613
614 /* v -- insert value of current cell */
615 case 'v': {
616 char valbuf[1024];
617
618 printvalue(valbuf,sizeof(valbuf),0,1,getscientific(sheet,sheet->curx,sheet->cury,sheet->curz),-1,sheet,sheet->curx,sheet->cury,sheet->curz);
619 if (strlen(buf)+strlen(valbuf) >= (size-1)) break;
620 (void)memmove(src+strlen(valbuf), src, strlen(src));
621 (void)memcpy(src, valbuf, strlen(valbuf));
622 (*x) += mbslen(valbuf);
623 break;
624 }
625
626 /* p -- insert position of current cell */
627 case 'p': {
628 char valbuf[1024];
629
630 sprintf(valbuf,"(%d,%d,%d)",sheet->curx,sheet->cury,sheet->curz);
631 if (strlen(buf)+strlen(valbuf) >= (size-1)) break;
632 (void)memmove(src+strlen(valbuf), src, strlen(src));
633 (void)memcpy(src, valbuf, strlen(valbuf));
634 (*x) += mbslen(valbuf);
635 break;
636 }
637
638 /* default -- move around in sheet */
639 default:
640 (void)do_sheetcmd(sheet,c,1);
641 redraw_sheet(sheet);
642 break;
643
644 } else switch (c) {
645 /* UP */
646 case K_UP: break;
647
648 /* LEFT */
649 case K_LEFT: if (*x > 0) (*x)--; break;
650
651 /* RIGHT */
652 case K_RIGHT: if (*x < mbslen(buf)) (*x)++; break;
653
654 /* BACKSPACE */
655 case K_BACKSPACE:
656 if (*x > 0) {
657 memmove(mbspos(src, -1), src, strlen(src)+1);
658 (*x)--;
659 }
660 break;
661
662 /* C-i -- file name completion */
663 case '\t':
664 completefile(buf, src, size);
665 break;
666
667 /* DC */
668 case K_DC:
669 src = mbspos(dest, 1);
670 if (*x < strlen(buf)) memmove(dest, src, strlen(src)+1);
671 break;
672
673 /* HOME */
674 case K_HOME:
675 *x = 0;
676 break;
677
678 /* END */
679 case K_END:
680 *x = mbslen(buf);
681 break;
682
683 /* IC */
684 case KEY_IC:
685 insert=1-insert;
686 break;
687
688 /* EIC */
689 case KEY_EIC:
690 insert=0;
691 break;
692
693 /* control t */
694 case '\024':
695 if (*x > 0) {
696 char c, *end;
697
698 dest = mbspos(src, -1);
699 if (*x == mbslen(buf)) {
700 src = dest;
701 dest = mbspos(src, -1);
702 (*x)--;
703 }
704 end = mbspos(src, 1);
705
706 while (src != end) {
707 c = *src;
708 memmove(dest+1, dest, src-dest);
709 *dest = c;
710 src++;
711 dest++;
712 }
713 (*x)++;
714 }
715 break;
716
717 /* control backslash */
718 case '\034': {
719 int level;
720 char open = 0, close = 0, dir = 1;
721
722 switch (*dest) {
723 case ')': dir = -1;
724 case '(': open = '('; close = ')'; break;
725 case '}': dir = -1;
726 case '{': open = '{'; close = '}'; break;
727 case ']': dir = -1;
728 case '[': open = '['; close = ']'; break;
729 default: break;
730 }
731
732 level = dir;
733 while (*dest && level) {
734 dest += dir;
735 if (*dest == open) level--;
736 else if (*dest == close) level++;
737 }
738 if (!level) *x = mbslen(buf)-mbslen(dest);
739 break;
740 }
741
742 /* DL */
743 case KEY_DL:
744 *src = '\0';
745 break;
746
747 /* control o */
748 case '\017':
749 if (sheet!=(Sheet*)0) sheet->moveonly=1;
750 break;
751
752 /* default */
753 default:
754 if (((unsigned int)c) < ' ' || ((unsigned int)c) >= 256) break;
755 if (strlen(buf) >= (size-1)) {
756 if (is_mbcont(c)) {
757 dest = mbspos(src, -1);
758 memmove(dest, src, strlen(src)+1);
759 }
760 break;
761 }
762 if (insert || is_mbcont(c)) memmove(src+1, src, strlen(src)+1);
763 else {
764 if (is_mbchar(*src)) memmove(src+1, mbspos(src, 1), strlen(mbspos(src, 1))+1);
765 if (!*src) *(src+1) = '\0';
766 }
767 *src = (char)c;
768 if (!is_mbcont(c)) (*x)++;
769 break;
770 }
771
772 } while (c != K_ENTER && c != KEY_CANCEL && (c != K_UP || (sheet!=(Sheet*)0 && sheet->moveonly)));
773
774 if (sheet) sheet->moveonly=0;
775 (void)curs_set(0);
776 (void)wmove(stdscr,LINES-1,0);
777 (void)wclrtoeol(stdscr);
778
779 switch (c) {
780 case KEY_CANCEL: return -1;
781 case K_UP: return -2;
782 default: return 0;
783 }
784 }
785
786 /* line_ok -- one line yes/no menu */
line_ok(const char * prompt,int curx)787 int line_ok(const char *prompt, int curx)
788 {
789
790 MenuChoice menu[3];
791 int result;
792
793
794
795 assert(curx==0 || curx==1);
796
797 menu[0].str=mystrmalloc(_("nN)o")); menu[0].c='\0';
798 menu[1].str=mystrmalloc(_("yY)es")); menu[1].c='\0';
799 menu[2].str=(char*)0;
800 result=line_menu(prompt,menu,curx);
801 free(menu[0].str);
802 free(menu[1].str);
803 return (result);
804 }
805
806 /* line_menu -- one line menu */
807 /* Notes */
808 /*
809
810 The choices are terminated by the last element having a (const char*)str
811 field. Each item can be chosen by tolower(*str) or by the key stored in
812 the c field. Use a space as first character of str, if you only want the
813 function key to work.
814
815 */
816
line_menu(const char * prompt,const MenuChoice * choice,int curx)817 int line_menu(const char *prompt, const MenuChoice *choice, int curx)
818 {
819
820 int maxx,x,width,offx;
821 chtype c;
822 size_t promptlen;
823
824
825
826 assert(prompt!=(const char*)0);
827 assert(choice!=(const MenuChoice*)0);
828 assert(curx>=0);
829
830 mvwaddstr(stdscr,LINES-1,0,prompt);
831 promptlen = mbslen(prompt);
832 for (maxx=0; (choice+maxx)->str!=(const char*)0; ++maxx);
833 offx=0;
834 do
835 {
836 (void)wmove(stdscr,LINES-1,(int)promptlen);
837 /* correct offset so choice is visible */
838 if (curx<=offx) offx=curx;
839 else do
840 {
841 for (width=promptlen,x=offx; x<maxx && width+((int)mbslen((choice+x)->str+1))+1<=COLS; width+=((int)(mbslen((choice+x)->str)))+1,++x);
842 --x;
843 if (x<curx) ++offx;
844 } while (x<curx);
845
846 /* show visible choices */
847 for (width=promptlen,x=offx; x<maxx && width+((int)mbslen((choice+x)->str+1))+1<=COLS; width+=mbslen((choice+x)->str+1)+1,++x)
848 {
849 (void)waddch(stdscr,(chtype)(unsigned char)' ');
850 if (x==curx) (void)wattron(stdscr,DEF_MENU);
851 (void)waddstr(stdscr,(char*)(choice+x)->str+1);
852 if (x==curx) (void)wattroff(stdscr,DEF_MENU);
853 }
854
855 if (width<COLS) (void)wclrtoeol(stdscr);
856 switch (c=wgetc())
857 {
858 /* KEY_LEFT -- move to previous item */
859 case K_BACKSPACE:
860 case K_LEFT: if (curx>0) --curx; else curx=maxx-1; break;
861
862 /* Space, Tab, KEY_RIGHT -- move to next item */
863 case ' ':
864 case '\t':
865 case K_RIGHT: if (curx<(maxx-1)) ++curx; else curx=0; break;
866
867 /* default -- search choice keys */
868 default:
869 {
870 int i;
871
872 for (i=0; (choice+i)->str!=(const char*)0; ++i)
873 {
874 if ((c<256 && tolower(c)==*((choice+i)->str)) || (choice+i)->c==c)
875 {
876 c=K_ENTER;
877 curx=i;
878 }
879 }
880 }
881
882 }
883 }
884 while (c!=K_ENTER && c!=K_DOWN && c!=KEY_CANCEL && c!=K_UP);
885 (void)wmove(stdscr,LINES-1,0);
886 (void)wclrtoeol(stdscr);
887 switch (c)
888 {
889 case KEY_CANCEL: return -1;
890 case K_UP: return -2;
891 default: return curx;
892 }
893 }
894
895 /* line_msg -- one line message which will be cleared by someone else */
line_msg(const char * prompt,const char * msg)896 void line_msg(const char *prompt, const char *msg)
897 {
898
899 int width;
900
901
902
903 assert(msg!=(const char*)0);
904 if (!*msg) msg = _("Use F0, F10 or / for menu");
905
906 if (!batch)
907 {
908 width=1;
909 mvwaddch(stdscr,LINES-1,0,(chtype)(unsigned char)'[');
910 if (prompt!=(const char*)0)
911 {
912 for (; width<COLS && *prompt!='\0'; ++width,++prompt) (void)waddch(stdscr,(chtype)(unsigned char)(*prompt));
913 if (width<COLS) { (void)waddch(stdscr,(chtype)(unsigned char)' '); ++width; }
914 }
915 for (; width<COLS && *msg!='\0'; ++width,++msg) (void)waddch(stdscr,(chtype)(unsigned char)(*msg));
916 if (width<COLS) (void)waddch(stdscr,(chtype)(unsigned char)']');
917 if (width+1<COLS) (void)wclrtoeol(stdscr);
918 }
919 else
920 {
921 if (prompt) fprintf(stderr,_("line %u: %s %s\n"),batchln,prompt,msg);
922 else fprintf(stderr,_("line %u: %s\n"),batchln,msg);
923 exit(1);
924 }
925 }
926
927
show_text(const char * text)928 void show_text(const char *text)
929 {
930 int i;
931 char *end, *stripped;
932
933 stripped = striphtml(text);
934 text = stripped-1;
935
936 while (text) {
937 (void)clear();
938 for (i = 0; i < LINES-2 && text; i++) {
939 end = strchr(++text, '\n');
940 if (*text == '\f') break;
941 if (end) *end = 0;
942 (void)move(i,(COLS-mbslen(text))/2);
943 (void)addstr(text);
944 text = end;
945 }
946 (void)move(i+1, (COLS-29)/2); (void)addstr(_("[ Press any key to continue ]"));
947 (void)refresh();
948 (void)getch();
949 }
950
951 free(stripped);
952 }
953
954 /* keypressed -- get keypress, if there is one */
keypressed(void)955 int keypressed(void)
956 {
957 (void)nodelay(stdscr,TRUE);
958 if (getch()==ERR)
959 {
960 (void)nodelay(stdscr,FALSE);
961 return 0;
962 }
963 else
964 {
965 (void)nodelay(stdscr,FALSE);
966 return 1;
967 }
968 }
969
970
971 /* wgetc */
wgetc(void)972 static Key wgetc(void)
973 {
974
975 chtype c;
976
977
978 doupdate();
979 refresh();
980 switch (c=wgetch(stdscr))
981 {
982 /* LEFT */
983 case KEY_LEFT:
984 case '\02': return K_LEFT;
985
986 /* RIGHT */
987 case KEY_RIGHT:
988 case '\06': return K_RIGHT;
989
990 /* UP */
991 case KEY_UP:
992 case '\020': return K_UP;
993
994 /* DOWN */
995 case KEY_DOWN:
996 case '\016': return K_DOWN;
997
998 /* BACKSPACE */
999 case KEY_BACKSPACE:
1000 case '\010': return K_BACKSPACE;
1001
1002 /* DC */
1003 case KEY_DC:
1004 case '\04':
1005 case '\177': return K_DC;
1006
1007 /* CANCEL */
1008 case '\03':
1009 case '\07': return KEY_CANCEL;
1010
1011 /* ENTER */
1012 case KEY_ENTER:
1013 case '\r':
1014 case '\n': return K_ENTER;
1015
1016 /* HOME */
1017 case KEY_HOME:
1018 case '\01': return K_HOME;
1019
1020 /* END */
1021 case KEY_END:
1022 case '\05': return K_END;
1023
1024 /* DL */
1025 case '\013': return KEY_DL;
1026
1027 /* NPAGE */
1028 case KEY_NPAGE:
1029 case '\026': return K_NPAGE;
1030
1031 /* PPAGE */
1032 case KEY_PPAGE: return K_PPAGE;
1033
1034 /* Control Y, copy */
1035 case '\031': return K_COPY;
1036
1037 /* Control R, recalculate sheet */
1038 case '\022': return K_RECALC;
1039
1040 /* Control S, clock sheet */
1041 case '\023': return K_CLOCK;
1042
1043 /* Control X, get one more key */
1044 case '\030':
1045 {
1046 switch (wgetch(stdscr))
1047 {
1048 /* C-x < -- BPAGE */
1049 case KEY_PPAGE:
1050 case '<': return K_BPAGE;
1051
1052 /* C-x > -- FPAGE */
1053 case KEY_NPAGE:
1054 case '>': return K_FPAGE;
1055
1056 /* C-x C-c -- QUIT */
1057 case '\03': return K_QUIT;
1058
1059 /* C-x C-s -- SAVE */
1060 case '\023': return K_SAVE;
1061
1062 /* C-x C-r -- LOAD */
1063 case '\022': return K_LOAD;
1064
1065 /* default -- INVALID, general invalid value */
1066 default: return K_INVALID;
1067
1068 }
1069 }
1070
1071 /* ESC, get one more key */
1072 case '\033':
1073 {
1074 switch (wgetch(stdscr))
1075 {
1076 /* M-v -- PPAGE */
1077 case 'v': return K_PPAGE;
1078
1079 /* M-Enter -- MENTER */
1080 case KEY_ENTER:
1081 case '\r':
1082 case '\n': return K_MENTER;
1083
1084 /* M-z -- SAVEQUIT */
1085 case 'z': return K_SAVEQUIT;
1086
1087 /* default -- INVALID, general invalid value */
1088 default: return K_INVALID;
1089
1090 }
1091 }
1092
1093 /* _("Load sheet file format:") */
1094 case KEY_F(2): return K_LOADMENU;
1095
1096 /* _("Save sheet file format:") */
1097 case KEY_F(3): return K_SAVEMENU;
1098
1099 /* default */
1100 default: return c;
1101
1102 }
1103 }
1104
1105
find_helpfile(char * buf,int size,const char * argv0)1106 void find_helpfile(char *buf, int size, const char *argv0)
1107 {
1108 strncpy(buf, HELPFILE, size);
1109 buf[size-1] = 0;
1110 }
1111