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