"Fossies" - the Fresh Open Source Software Archive

Member "teapot-2.3.0/display.c" (6 Feb 2012, 26279 Bytes) of package /linux/privat/old/teapot-2.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "display.c" see the Fossies "Dox" file reference documentation.

    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 */
   50 static void redraw(void)
   51 {
   52   (void)touchwin(curscr);
   53   (void)wrefresh(curscr);
   54 }
   55 
   56 
   57 /* do_attribute   -- set cell attributes */
   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 */
  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 */
  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 */
  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 
  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 */
  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 
  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 
  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 
  400 void display_end(void)
  401 {
  402   curs_set(1);
  403   echo();
  404   noraw();
  405   refresh();
  406   endwin();
  407 }
  408 
  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 */
  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 */
  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 */
  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 */
  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 
  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 */
  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 
  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 */
  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 */
  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 
 1106 void find_helpfile(char *buf, int size, const char *argv0)
 1107 {
 1108     strncpy(buf, HELPFILE, size);
 1109     buf[size-1] = 0;
 1110 }