home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Product / Product.zip / sc621_3.zip / src / sc.c < prev    next >
C/C++ Source or Header  |  1993-11-21  |  34KB  |  1,489 lines

  1.  /*    SC    A Spreadsheet Calculator
  2.  *        Main driver
  3.  *
  4.  *        original by James Gosling, September 1982
  5.  *        modifications by Mark Weiser and Bruce Israel,
  6.  *            University of Maryland
  7.  *
  8.  *              More mods Robert Bond, 12/86
  9.  *        More mods by Alan Silverstein, 3-4/88, see list of changes.
  10.  *        Currently supported by sequent!sawmill!buhrt (Jeff Buhrt)
  11.  *        $Revision: 6.21 $
  12.  *
  13.  */
  14.  
  15. #include <sys/types.h>
  16. #include <signal.h>
  17. #include <curses.h>
  18. #include <ctype.h>
  19.  
  20. #ifdef BSD42
  21. #include <strings.h>
  22. #else
  23. #ifndef SYSIII
  24. #include <string.h>
  25. #endif
  26. #endif
  27.  
  28. #include <stdio.h>
  29. #include "sc.h"
  30. #if defined(MSDOS) || defined(__OS2__)
  31. #include <stdlib.h>    /* Use _splitpath to get progname */
  32. #include <process.h>   /* use spawn instead of fork     */
  33. #endif
  34.  
  35. extern    char    *getenv();
  36. extern    void    startdisp(), stopdisp();
  37.  
  38. #ifdef SYSV3
  39. void exit();
  40. #endif
  41.  
  42. #ifndef SAVENAME
  43. #define    SAVENAME "SC.SAVE" /* file name to use for emergency saves */
  44. #endif /* SAVENAME */
  45.  
  46. #ifndef DFLT_PAGER
  47. #define    DFLT_PAGER "more"    /* more is probably more widespread than less */
  48. #endif /* DFLT_PAGER */
  49.  
  50. #define MAXCMD 160    /* for ! command below */
  51.  
  52. /* Globals defined in sc.h */
  53.  
  54. struct ent ***tbl;
  55. int strow = 0, stcol = 0;
  56. int currow = 0, curcol = 0;
  57. int savedrow, savedcol;
  58. int FullUpdate = 0;
  59. int ClearScreen = 0;    /* don't try to be smart */
  60. int maxrow, maxcol;
  61. int maxrows, maxcols;
  62. int *fwidth;
  63. int *precision;
  64. int *realfmt;
  65. char *col_hidden;
  66. char *row_hidden;
  67. char line[FBUFLEN];
  68. int changed;
  69. struct ent *to_fix;
  70. int modflg;
  71. int numeric;
  72. char *mdir;
  73. int showsc, showsr;    /* Starting cell for highlighted range */
  74. #ifdef RIGHT_CBUG
  75. int    wasforw    = FALSE;
  76. #endif
  77.  
  78. void    update();
  79. void    repaint();
  80.  
  81. char curfile[PATHLEN];
  82. char    revmsg[80];
  83.  
  84. int  linelim = -1;
  85.  
  86. int  showtop   = 1;    /* Causes current cell value display in top line  */
  87. int  showcell  = 1;    /* Causes current cell to be highlighted      */
  88. int  showrange = 0;    /* Causes ranges to be highlighted          */
  89. int  showneed  = 0;    /* Causes cells needing values to be highlighted  */
  90. int  showexpr  = 0;    /* Causes cell exprs to be displayed, highlighted */
  91.  
  92. int  autocalc = 1 ;    /* 1 to calculate after each update */
  93. int  autolabel = 1;     /* If room, causes label to be created after a define*/
  94. int  calc_order = BYROWS;
  95. int  tbl_style = 0;    /* headers for T command output */
  96. int  rndinfinity = 0;
  97. int  numeric_field = 0; /* Started the line editing with a number */
  98. int  craction = 0;    /* 1 for down, 2 for right */
  99. int  rowlimit = -1;
  100. int  collimit = -1;
  101. #ifdef    SIGWINCH
  102. int  hitwinch = 0;    /* got a SIGWINCH? */
  103. #endif
  104.  
  105. extern    int lastmx, lastmy;    /* Screen address of the cursor */
  106. extern    int lastcol, lcols;    /* Spreadsheet Column the cursor was in last */
  107.  
  108. /* a linked list of free [struct ent]'s, uses .next as the pointer */
  109. struct ent *freeents = NULL;
  110.  
  111. extern    int    seenerr;
  112. extern    char    *rev;
  113.  
  114. #ifdef VMS
  115. int VMS_read_raw = 0;
  116. #endif
  117.  
  118. /* return a pointer to a cell's [struct ent *], creating if needed */
  119. struct ent *
  120. lookat(row,col)
  121. int    row, col;
  122. {
  123.     register struct ent **pp;
  124.  
  125.     checkbounds(&row, &col);
  126.     pp = ATBL(tbl, row, col);
  127.     if (*pp == (struct ent *)0) {
  128.         if (freeents != NULL)
  129.     {    *pp = freeents;
  130.         freeents = freeents->next;
  131.     }
  132.     else
  133.         *pp = (struct ent *) scxmalloc((unsigned)sizeof(struct ent));
  134.     if (row>maxrow) maxrow = row;
  135.     if (col>maxcol) maxcol = col;
  136.     (*pp)->label = (char *)0;
  137.     (*pp)->row = row;
  138.     (*pp)->col = col;
  139.     (*pp)->flags = 0;
  140.     (*pp)->expr = (struct enode *)0;
  141.     (*pp)->v = (double) 0.0;
  142.     (*pp)->format = (char *)0;
  143.     (*pp)->cellerror = CELLOK;
  144.     (*pp)->next = NULL;
  145.     }
  146.     return *pp;
  147. }
  148.  
  149. /*
  150.  * This structure is used to keep ent structs around before they
  151.  * are deleted to allow the sync_refs routine a chance to fix the
  152.  * variable references.
  153.  * We also use it as a last-deleted buffer for the 'p' command.
  154.  */
  155. void
  156. free_ent(p)
  157. register struct ent *p;
  158. {
  159.     p->next = to_fix;
  160.     to_fix = p;
  161.     p->flags |= is_deleted;
  162.     p->flags &= ~is_locked;
  163. }
  164.  
  165. /* free deleted cells */
  166. void
  167. flush_saved()
  168. {
  169.     register struct ent *p;
  170.     register struct ent *q;
  171.  
  172.     if (!(p = to_fix))
  173.     return;
  174.     while (p) {
  175.     (void) clearent(p);
  176.     q = p->next;
  177.     p->next = freeents;    /* put this ent on the front of freeents */
  178.     freeents = p;
  179.     p = q;
  180.     }
  181.     to_fix = NULL;
  182. }
  183.  
  184. void *
  185. default_file_is(filename, ext)
  186. char *filename, *ext;
  187. {
  188.   extern char *add_ext();
  189.   char path[256];
  190.  
  191.   strcpy(path, filename);
  192.   error ("Default file is \"%s\"",add_ext(path, ext));
  193. }
  194.  
  195. char    *progname;
  196.  
  197. int
  198. main (argc, argv)
  199. int argc;
  200. char  **argv;
  201. {
  202.     int     inloop = 1;
  203.     register int   c;
  204.     int     edistate = -1;
  205.     int     arg = 1;
  206.     int     narg;
  207.     int     nedistate;
  208.     int        running;
  209.     char    *revi;
  210.     int        anychanged = FALSE;
  211.  
  212.     /*
  213.      * Keep command line options around until the file is read so the
  214.      * command line overrides file options
  215.      */
  216.  
  217.     int Mopt = 0;
  218.     int Nopt = 0;
  219.     int Copt = 0; 
  220.     int Ropt = 0;
  221.  
  222.     int tempx, tempy;     /* Temp versions of curx, cury */
  223.  
  224. #if defined(MSDOS) || defined(__OS2__)
  225.     static char fname[_MAX_FNAME];
  226.     _splitpath(argv[0], NULL, NULL, progname = fname, NULL);
  227. #else
  228. #ifdef VMS
  229.     if ((revi = strrchr(argv[0], ']')) != NULL)
  230. #else
  231.     if ((revi = strrchr(argv[0], '/')) != NULL)
  232. #endif
  233.     progname = revi+1;
  234.     else
  235.     progname = argv[0];
  236. #endif
  237.  
  238.     while (argc > 1 && argv[1][0] == '-') {
  239.     argv++;
  240.     argc--;
  241.         switch (argv[0][1]) {
  242.         case 'x':
  243. #if defined(VMS) || defined(MSDOS) || !defined(CRYPT_PATH)
  244.             (void) fprintf(stderr, "Crypt not available\n");
  245.             exit(1);
  246. #else 
  247.             Crypt = 1;
  248. #endif
  249.             break;
  250.         case 'm':
  251.             Mopt = 1;
  252.             break;
  253.         case 'n':
  254.             Nopt = 1;
  255.             break;
  256.         case 'c':
  257.             Copt = 1;
  258.             break;
  259.         case 'r':
  260.             Ropt = 1;
  261.             break;
  262.         case 'C':
  263.             craction = CRCOLS;
  264.             break;
  265.         case 'R':
  266.             craction = CRROWS;
  267.             break;
  268.         default:
  269.             (void) fprintf(stderr,"%s: unrecognized option: \"%c\"\n",
  270.             progname,argv[0][1]);
  271.             exit(1);
  272.     }
  273.     }
  274.  
  275.     *curfile ='\0';
  276.  
  277.     startdisp();
  278.     signals();
  279.  
  280.     /* setup the spreadsheet arrays, initscr() will get the screen size */
  281.     if (!growtbl(GROWNEW, 0, 0))
  282.     {    stopdisp();
  283.     exit(1);
  284.     }
  285.  
  286.     /*
  287.      * Build revision message for later use:
  288.      */
  289.  
  290.     (void) strcpy (revmsg, progname);
  291.     for (revi = rev; (*revi++) != ':'; );    /* copy after colon */
  292.     (void) strcat (revmsg, revi);
  293.     revmsg [strlen (revmsg) - 2] = 0;        /* erase last character */
  294.     (void) strcat (revmsg, ":  Type '?' for help.");
  295.  
  296.     if (argc > 1) {
  297.     (void) strcpy(curfile,argv[1]);
  298.     readfile (argv[1], 0);
  299.     }
  300.  
  301.     if (Mopt)
  302.     autocalc = 0;
  303.     if (Nopt)
  304.     numeric = 1;
  305.     if (Copt)
  306.     calc_order = BYCOLS;
  307.     if (Ropt)
  308.     calc_order = BYROWS;
  309.  
  310.     modflg = 0;
  311. #ifdef VENIX
  312.     setbuf (stdin, NULL);
  313. #endif
  314.     FullUpdate++;
  315.  
  316.     while (inloop) { running = 1;
  317.     while (running) {
  318.     nedistate = -1;
  319.     narg = 1;
  320.     if (edistate < 0 && linelim < 0 && autocalc && (changed || FullUpdate))
  321.     {    EvalAll ();
  322.          if (changed)        /* if EvalAll changed or was before */
  323.         anychanged = TRUE;
  324.          changed = 0;
  325.     }
  326.     else        /* any cells change? */
  327.     if (changed)
  328.          anychanged = TRUE;
  329.  
  330. #ifdef    SIGWINCH
  331.     /* got a SIGWINCH? */
  332.     if (hitwinch)
  333.     {    hitwinch = 0;
  334.         stopdisp();
  335.         startdisp();
  336.         FullUpdate++;
  337.     }
  338. #endif
  339.     update(anychanged);
  340.     anychanged = FALSE;
  341. #ifndef SYSV3    /* HP/Ux 3.1 this may not be wanted */
  342.     (void) refresh(); /* 5.3 does a refresh in getch */ 
  343. #endif
  344.     c = nmgetch();
  345.     getyx(stdscr, tempy, tempx);
  346.     (void) move (1, 0);
  347.     (void) clrtoeol ();
  348.     (void) move(tempy, tempx);
  349. /*    (void) fflush (stdout);*/
  350.     seenerr = 0;
  351.     showneed = 0;    /* reset after each update */
  352.     showexpr = 0;
  353.  
  354.     /*
  355.      * there seems to be some question about what to do w/ the iscntrl
  356.      * some BSD systems are reportedly broken as well
  357.      */
  358.     /* if ((c < ' ') || ( c == DEL ))   how about international here ? PB */
  359. #if    pyr
  360.        if ( iscntrl(c) || (c >= 011 && c <= 015) )    /* iscntrl broken in OSx4.1 */
  361. #else
  362.        if (isascii(c) && (iscntrl(c) || (c == 020)) )    /* iscntrl broken in OSx4.1 */
  363. #endif
  364.         switch (c) {
  365. #ifdef SIGTSTP
  366.         case ctl('z'):
  367.             (void) deraw();
  368.             (void) kill(0, SIGTSTP); /* Nail process group */
  369.  
  370.             /* the pc stops here */
  371.  
  372.             (void) goraw();
  373.             break;
  374. #endif
  375.         case ctl('r'):
  376.             showneed = 1;
  377.         case ctl('l'):
  378.             FullUpdate++;
  379.             ClearScreen++;
  380.             (void) clearok(stdscr,1);
  381.             /* Centering the display with cursor for middle */
  382.             if(currow > (LINES-RESROW)/2)
  383.             strow = currow - ((LINES-RESROW)/2);
  384.             break;
  385.         case ctl('x'):
  386.             FullUpdate++;
  387.             showexpr = 1;
  388.             (void) clearok(stdscr,1);
  389.             break;
  390.         default:
  391.             error ("No such command (^%c)", c + 0100);
  392.             break;
  393.         case ctl('b'):
  394.             if (numeric_field) {
  395.             write_line(ctl('m'));
  396.             numeric_field = 0;
  397.             }
  398.             backcol(arg);
  399.             break;
  400.         case ctl('c'):
  401.             running = 0;
  402.             break;
  403.  
  404.         case ctl('e'):
  405.  
  406.             switch (nmgetch()) {
  407.             case ctl('p'): case 'k':    doend (-1, 0);    break;
  408.             case ctl('n'): case 'j':    doend ( 1, 0);    break;
  409.             case ctl('b'): case 'h':
  410.             case ctl('h'):        doend ( 0,-1);    break;
  411.             case ctl('f'): case 'l':
  412.             case ctl('i'): case ' ':    doend ( 0, 1);    break;
  413.  
  414.             case ESC:
  415.             case ctl('g'):
  416.             break;
  417.  
  418.             default:
  419.             error("Invalid ^E command");
  420.             break;
  421.             }
  422.  
  423.             break;
  424.  
  425.         case ctl('f'):
  426.             if (numeric_field) {
  427.             write_line(ctl('m'));
  428.             numeric_field = 0;
  429.             }
  430.             forwcol(arg);
  431. #ifdef RIGHT_CBUG
  432.             wasforw++;
  433. #endif
  434.             break;
  435.  
  436.         case ctl('g'):
  437.             showrange = 0;
  438.             linelim = -1;
  439.             (void) move (1, 0);
  440.             (void) clrtoeol ();
  441.             break;
  442.  
  443.         case ESC:    /* ctl('[') */
  444.             write_line(ESC);
  445.             break;
  446.  
  447.         case ctl('d'):
  448.             write_line(ctl('d'));
  449.             break;
  450.  
  451.         case DEL:
  452.         case ctl('h'):
  453.             if (linelim < 0) {    /* not editing line */
  454.             backcol(arg);    /* treat like ^B    */
  455.             break;
  456.             }
  457.             write_line(ctl('h'));
  458.             break;
  459.  
  460.         case ctl('i'):         /* tab */
  461.             if (linelim < 0) {    /* not editing line */
  462.             forwcol(arg);
  463.             break;
  464.             }
  465.             if (!showrange) {
  466.             startshow();
  467.             } else {
  468.             showdr();
  469.             linelim = strlen(line);
  470.             line[linelim++] = ' ';
  471.             line[linelim] = '\0';
  472.             showrange = 0;
  473.             }
  474.             linelim = strlen (line);
  475.             break;
  476.  
  477.         case ctl('m'):
  478.         case ctl('j'):
  479.             numeric_field = 0;
  480.             write_line(ctl('m'));
  481.             switch(craction) {
  482.               case CRROWS:
  483.             if ((rowlimit >= 0) && (currow >= rowlimit)) {
  484.                 forwcol(1);
  485.                 currow = 0;
  486.             }
  487.             else {
  488.                 forwrow(1);
  489.             }
  490.             break;
  491.               case CRCOLS:
  492.             if ((collimit >= 0) && (curcol >= collimit)) {
  493.                 forwrow(1);
  494.                 curcol = 0;
  495.             }
  496.             else {
  497.                 forwcol(1);
  498.             }
  499.             break;
  500.               default:
  501.             break;
  502.               }
  503.             break;
  504.  
  505.         case ctl('n'):
  506.             if (numeric_field) {
  507.             write_line(ctl('m'));
  508.             numeric_field = 0;
  509.             }
  510.             forwrow(arg);
  511.             break;
  512.  
  513.         case ctl('p'):
  514.             if (numeric_field) {
  515.             write_line(ctl('m'));
  516.             numeric_field = 0;
  517.             }
  518.             backrow(arg);
  519.             break;
  520.  
  521.         case ctl('q'):
  522.             break;    /* ignore flow control */
  523.  
  524.         case ctl('s'):
  525.             break;    /* ignore flow control */
  526.  
  527.         case ctl('t'):
  528. #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH)
  529.             error(
  530. "Toggle: a:auto,c:cell,e:ext funcs,n:numeric,t:top,x:encrypt,$:pre-scale,<MORE>");
  531. #else                 /* no encryption available */
  532.             error(
  533. "Toggle: a:auto,c:cell,e:ext funcs,n:numeric,t:top,$:pre-scale,<MORE>");
  534. #endif
  535.             (void) refresh();
  536.  
  537.             switch (nmgetch()) {
  538.             case 'a': case 'A':
  539.             case 'm': case 'M':
  540.                 autocalc ^= 1;
  541.                 error("Automatic recalculation %sabled.",
  542.                 autocalc ? "en":"dis");
  543.                 break;
  544.             case 'n': case 'N':
  545.                 numeric = (! numeric);
  546.                 error ("Numeric input %sabled.",
  547.                     numeric ? "en" : "dis");
  548.                 break;
  549.             case 't': case 'T':
  550.                 showtop = (! showtop);
  551.                 error ("Top line %sabled.", showtop ? "en" : "dis");
  552.                 break;
  553.             case 'c': case 'C':
  554.                 showcell = (! showcell);
  555.                 repaint(lastmx, lastmy, fwidth[lastcol]);
  556.                 error ("Cell highlighting %sabled.",
  557.                     showcell ? "en" : "dis");
  558.                 break;
  559.             case 'x': case 'X':
  560. #if defined(VMS) || defined(MSDOS) || !defined(CRYPT_PATH)
  561.                 error ("Encryption not available.");
  562. #else 
  563.                 Crypt = (! Crypt);
  564.                 error ("Encryption %sabled.", Crypt? "en" : "dis");
  565. #endif
  566.                 break;
  567.             case 'l': case 'L':
  568.                 autolabel = (! autolabel);
  569.                 error ("Autolabel %sabled.",
  570.                    autolabel? "en" : "dis");
  571.                 break;
  572.             case '$':
  573.                 if (prescale == 1.0) {
  574.                 error ("Prescale enabled.");
  575.                 prescale = 0.01;
  576.                 } else {
  577.                 prescale = 1.0;
  578.                 error ("Prescale disabled.");
  579.                 }
  580.                 break;
  581.             case 'e': case 'E':
  582.                 extfunc = (! extfunc);
  583.                 error ("External functions %sabled.",
  584.                     extfunc? "en" : "dis");
  585.                 break;
  586.             case ESC:
  587.             case ctl('g'):
  588.                 --modflg;    /* negate the modflg++ */
  589.                 break;
  590.             case 'r': case 'R':
  591.                 ++craction;
  592.                 if(craction >= 3)
  593.                 craction = 0;
  594.                 switch(craction) {
  595.                 default:
  596.                     craction = 0; /* fall through */
  597.                 case 0:
  598.                     error("No action after new line");
  599.                     break;
  600.                 case CRROWS:
  601.                     error("Down row after new line");
  602.                     break;
  603.                 case CRCOLS:
  604.                     error("Right column after new line");
  605.                     break;
  606.                 }
  607.                 break;
  608.             case 'z': case 'Z':
  609.                 rowlimit = currow;
  610.                 collimit = curcol;
  611.                 error("Row and column limits set");
  612.                 break;
  613.             default:
  614.                 error ("Invalid toggle command");
  615.                 --modflg;    /* negate the modflg++ */
  616.             }
  617.             FullUpdate++;
  618.             modflg++;
  619.             break;
  620.  
  621.         case ctl('u'):
  622.             narg = arg * 4;
  623.             nedistate = 1;
  624.             break;
  625.  
  626.         case ctl('v'):    /* insert variable name */
  627.             if (linelim > 0)
  628.                 ins_string(v_name(currow, curcol));
  629.             break;
  630.  
  631.         case ctl('w'):    /* insert variable expression */
  632.             if (linelim > 0)  {
  633.             static    char *temp = NULL, *temp1 = NULL;
  634.             static    unsigned    templen = 0;
  635.             int templim;
  636.  
  637.             /* scxrealloc will scxmalloc if needed */
  638.             if (strlen(line)+1 > templen)
  639.             {    templen = strlen(line)+40;
  640.  
  641.                 temp = scxrealloc(temp, templen);
  642.                 temp1= scxrealloc(temp1, templen);
  643.             }
  644.             strcpy(temp, line);
  645.             templim = linelim;
  646.             linelim = 0;        /* reset line to empty    */
  647.             editexp(currow,curcol);
  648.             strcpy(temp1, line);
  649.             strcpy(line, temp);
  650.             linelim = templim;
  651.             ins_string(temp1);
  652.             }
  653.             break;
  654.  
  655.         case ctl('a'):    /* insert variable value */
  656.             if (linelim > 0) {
  657.             struct ent *p = *ATBL(tbl, currow, curcol);
  658.             char temp[100];
  659.  
  660.             if (p && p -> flags & is_valid) {
  661.                 (void) sprintf (temp, "%.*f",
  662.                     precision[curcol],p -> v);
  663.                 ins_string(temp);
  664.             }
  665.             }
  666.             break;
  667.  
  668.         } /* End of the control char switch stmt */
  669.     else if (isascii(c) && isdigit(c) && ((numeric && edistate >= 0) ||
  670.             (!numeric && (linelim < 0 || edistate >= 0)))) {
  671.         /* we got a leading number */
  672.         if (edistate != 0) {
  673.         /* First char of the count */
  674.         if (c == '0')      /* just a '0' goes to left col */
  675.             curcol = 0;
  676.         else {
  677.             nedistate = 0;
  678.             narg = c - '0';
  679.         }
  680.         } else {
  681.         /* Succeeding count chars */
  682.         nedistate = 0;
  683.         narg = arg * 10 + (c - '0');
  684.         }
  685.     } else if (linelim >= 0) {
  686.         /* Editing line */
  687.         switch(c) {
  688.         case ')':
  689.         if (showrange) {
  690.             showdr();
  691.             showrange = 0;
  692.             linelim = strlen (line);
  693.         }
  694.         break;
  695.         default:
  696.         break;
  697.         }
  698.         write_line(c);
  699.  
  700.     } else if (!numeric && ( c == '+' || c == '-' ) ) {
  701.         /* increment/decrement ops */
  702.         register struct ent *p = *ATBL(tbl, currow, curcol);
  703.         if (!p)
  704.         continue;
  705.         if (p->expr && !(p->flags & is_strexpr)) {
  706.         error("Can't increment/decrement a formula\n");
  707.         continue;
  708.         }
  709.         FullUpdate++;
  710.         modflg++;
  711.         if( c == '+' )
  712.             p -> v += (double) arg;
  713.         else
  714.         p -> v -= (double) arg;
  715.     } else
  716.         /* switch on a normal command character */
  717.         switch (c) {
  718.         case ':':
  719.             break;    /* Be nice to vi users */
  720.  
  721.         case '@':
  722.             EvalAll ();
  723.             changed = 0;
  724.             anychanged = TRUE;
  725.             break;
  726.  
  727.         case '0': case '1': case '2': case '3': case '4':
  728.         case '5': case '6': case '7': case '8': case '9':
  729.         case '-': case '.': case '+':
  730.             if (locked_cell(currow, curcol))
  731.             break;
  732.             numeric_field = 1;
  733.             (void) sprintf(line,"let %s = %c",
  734.                 v_name(currow, curcol), c);
  735.             linelim = strlen (line);
  736.             insert_mode();
  737.             break;
  738.  
  739.         case '=':
  740.             if (locked_cell(currow, curcol))
  741.             break;
  742.             (void) sprintf(line,"let %s = ",
  743.                     v_name(currow, curcol));
  744.             linelim = strlen (line);
  745.             insert_mode();
  746.             break;
  747.  
  748.         case '!':
  749.             {
  750.             /*
  751.              *  "! command"  executes command
  752.              *  "!"    forks a shell
  753.              *  "!!" repeats last command
  754.              */
  755. #if defined(VMS)
  756.             error("Not implemented");
  757. #else /* VMS */
  758.             char *shl, *opt;
  759.             int pid, temp;
  760.             char cmd[MAXCMD];
  761.             static char lastcmd[MAXCMD];
  762.  
  763. #if defined(MSDOS) || defined(__OS2__)
  764.             shellbuf shell;
  765.  
  766.             shl = getshell(&shell);
  767.             opt = shell.opt;
  768. #else
  769.             if (!(shl = getenv("SHELL")))
  770.             shl = "/bin/sh";
  771.             opt = "-c";
  772. #endif
  773.             deraw();
  774.             (void) fputs("! ", stdout);
  775.             (void) fflush(stdout);
  776.             (void) fgets(cmd, MAXCMD, stdin);
  777.             cmd[strlen(cmd) - 1] = '\0';    /* clobber \n */
  778.             if(strcmp(cmd,"!") == 0)        /* repeat? */
  779.                 (void) strcpy(cmd, lastcmd);
  780.             else
  781.                 (void) strcpy(lastcmd, cmd);
  782.  
  783.             if (modflg)
  784.             {
  785.             (void) puts ("[No write since last change]");
  786.             (void) fflush (stdout);
  787.             }
  788.  
  789. #if defined(MSDOS) || defined(__OS2__)
  790.             if(strlen(cmd))
  791.               (void) spawnl(P_WAIT, shl, shl, opt, cmd, (char *)0);
  792.             else
  793.               (void) spawnl(P_WAIT, shl, shl, (char *)0);
  794. #else
  795.             if (!(pid = fork()))
  796.             {
  797.             (void) signal (SIGINT, SIG_DFL);  /* reset */
  798.             if(strlen(cmd))
  799.                 (void)execl(shl,shl,opt,cmd,(char *)0);
  800.             else
  801.                 (void) execl(shl, shl, (char *)0);
  802.             exit(-127);
  803.             }
  804.  
  805.             while (pid != wait(&temp));
  806. #endif
  807.             (void) printf("Press RETURN to continue ");
  808.             fflush(stdout);
  809.             (void)nmgetch();
  810.             goraw();
  811. #endif /* VMS */
  812.             break;
  813.             }
  814.  
  815.         /*
  816.          * Range commands:
  817.          */
  818.  
  819.         case '/':
  820.             error (
  821. "Range: x:erase v:value c:copy f:fill d:def l:lock U:unlock s:show u:undef F:fmt");
  822.             (void) refresh();
  823.  
  824.             switch (nmgetch()) {
  825.             case 'l':
  826.             (void) sprintf(line,"lock [range] ");
  827.             linelim = strlen(line);
  828.             startshow();
  829.             insert_mode();
  830.             break;
  831.             case 'U':
  832.             (void) sprintf(line,"unlock [range] ");
  833.             linelim = strlen(line);
  834.             startshow();
  835.             insert_mode();
  836.             break;
  837.             case 'c':
  838.             (void) sprintf(line,"copy [dest_range src_range] ");
  839.             linelim = strlen(line);
  840.             startshow();
  841.             insert_mode();
  842.             break;
  843.             case 'x':
  844.             (void) sprintf(line,"erase [range] ");
  845.             linelim = strlen(line);
  846.             startshow();
  847.             insert_mode();
  848.             break;
  849.             case 'v':
  850.             (void) sprintf(line, "value [range] ");
  851.             linelim = strlen(line);
  852.             startshow();
  853.             insert_mode();
  854.             break;
  855.             case 'f':
  856.             (void) sprintf(line,"fill [range start inc] ");
  857.             linelim = strlen(line);
  858.             startshow();
  859.             insert_mode();
  860.             break;
  861.             case 'd':
  862.             (void) sprintf(line,"define [string range] \"");
  863.             linelim = strlen(line);
  864.             startshow();
  865.             insert_mode();
  866.             modflg++;
  867.             break;
  868.             case 'u':
  869.             (void) sprintf(line,"undefine [range] ");
  870.             linelim = strlen(line);
  871.             insert_mode();
  872.             modflg++;
  873.             break;
  874.             case 's':
  875.             if(are_ranges())
  876.             {
  877.             FILE *f;
  878.             int pid;
  879.             char px[MAXCMD] ;
  880.             char *pager;
  881.  
  882.             (void) strcpy(px, "| sort | ");
  883.             if(!(pager = getenv("PAGER")))
  884.                 pager = DFLT_PAGER;
  885.             (void) strcat(px,pager);
  886.             f = openout(px, &pid);
  887.             if (!f) {
  888.                 error("Can't open pipe to sort");
  889.                 break;
  890.             }
  891.             list_range(f);
  892.             closeout(f, pid);
  893.             }
  894.             else error("No ranges defined");
  895.             break;
  896.             case 'F':
  897.             (void) sprintf(line, "fmt [range \"format\"] ");
  898.             linelim = strlen(line);
  899.             startshow();
  900.             insert_mode();
  901.             break;
  902.             case ESC:
  903.             case ctl('g'):
  904.             break;
  905.            default:
  906.             error("Invalid region command");
  907.             break;
  908.            }
  909.            break;
  910.  
  911.         /*
  912.          * Row/column commands:
  913.          */
  914.  
  915.         case 'i':
  916.         case 'a':
  917.         case 'd':
  918.         case 'p':
  919.         case 'v':
  920.         case 'z':
  921.         case 's':
  922.             {
  923.             register rcqual;
  924.  
  925.             if (! (rcqual = get_rcqual (c))) {
  926.                 error ("Invalid row/column command");
  927.                 break;
  928.             }
  929.  
  930.             error ("");    /* clear line */
  931.  
  932.             if ( rcqual == ESC || rcqual == ctl('g'))
  933.                 break;
  934.  
  935.             switch (c) {
  936.  
  937.             case 'i':
  938.                 if (rcqual == 'r')    insertrow(arg);
  939.                 else        opencol(curcol, arg);
  940.                 break;
  941.  
  942.             case 'a':
  943.                 if (rcqual == 'r')    while (arg--) duprow();
  944.                 else        while (arg--) dupcol();
  945.                 break;
  946.  
  947.             case 'd':
  948.                 if (rcqual == 'r')    deleterow(arg);
  949.                 else        closecol(curcol, arg);
  950.                 break;
  951.  
  952.             case 'p':
  953.                 while (arg--)    pullcells(rcqual);
  954.                 break;
  955.  
  956.             /*
  957.              * turn an area starting at currow/curcol into
  958.              * constants vs expressions - not reversable
  959.              */
  960.             case 'v':
  961.                 if (rcqual == 'r')
  962.                 valueize_area(currow, 0,
  963.                           currow + arg - 1, maxcol);
  964.                 else
  965.                 valueize_area(0, curcol,
  966.                           maxrow, curcol + arg - 1);
  967.                 modflg = 1;
  968.                 break;
  969.  
  970.             case 'z':
  971.                 if (rcqual == 'r')    hiderow(arg);
  972.                 else        hidecol(arg);
  973.                 break;
  974.  
  975.             case 's':
  976.                 /* special case; no repeat count */
  977.  
  978.                 if (rcqual == 'r')    rowshow_op();
  979.                 else        colshow_op();
  980.                 break;
  981.             }
  982.             break;
  983.             }
  984.  
  985.         case '$':
  986.             {
  987.             register struct ent *p;
  988.  
  989.             curcol = maxcols - 1;
  990.             while (!VALID_CELL(p, currow, curcol) && curcol > 0)
  991.             curcol--;
  992.             break;
  993.             }
  994.         case '#':
  995.             {
  996.             register struct ent *p;
  997.  
  998.             currow = maxrows - 1;
  999.             while (!VALID_CELL(p, currow, curcol) && currow > 0)
  1000.             currow--;
  1001.             break;
  1002.             }
  1003.         case 'w':
  1004.             {
  1005.             register struct ent *p;
  1006.  
  1007.             while (--arg>=0) {
  1008.             do {
  1009.                 if (curcol < maxcols - 1)
  1010.                 curcol++;
  1011.                 else {
  1012.                 if (currow < maxrows - 1) {
  1013.                     while(++currow < maxrows - 1 &&
  1014.                         row_hidden[currow]) /* */;
  1015.                     curcol = 0;
  1016.                 } else {
  1017.                     error("At end of table");
  1018.                     break;
  1019.                 }
  1020.                 }
  1021.             } while(col_hidden[curcol] ||
  1022.                 !VALID_CELL(p, currow, curcol));
  1023.             }
  1024.             break;
  1025.             }
  1026.         case 'b':
  1027.             {
  1028.             register struct ent *p;
  1029.  
  1030.             while (--arg>=0) {
  1031.             do {
  1032.                 if (curcol) 
  1033.                 curcol--;
  1034.                 else {
  1035.                 if (currow) {
  1036.                     while(--currow &&
  1037.                     row_hidden[currow]) /* */;
  1038.                     curcol = maxcols - 1;
  1039.                 } else {
  1040.                     error ("At start of table");
  1041.                     break;
  1042.                 }
  1043.                 }
  1044.             } while(col_hidden[curcol] ||
  1045.                 !VALID_CELL(p, currow, curcol));
  1046.             }
  1047.             break;
  1048.             }
  1049.         case '^':
  1050.             currow = 0;
  1051.             break;
  1052.         case '?':
  1053.             help();
  1054.             break;
  1055.         case '"':
  1056.             if (!locked_cell(currow, curcol)) {
  1057.                (void) sprintf (line, "label %s = \"",
  1058.                        v_name(currow, curcol));
  1059.                linelim = strlen (line);
  1060.                insert_mode();
  1061.             }
  1062.             break;
  1063.  
  1064.         case '<':
  1065.             if (!locked_cell(currow, curcol)) {
  1066.                (void) sprintf (line, "leftstring %s = \"",
  1067.                    v_name(currow, curcol));
  1068.                linelim = strlen (line);
  1069.                insert_mode();
  1070.             }
  1071.             break;
  1072.  
  1073.         case '>':
  1074.             if (!locked_cell(currow, curcol)) {
  1075.                (void) sprintf (line, "rightstring %s = \"",
  1076.                   v_name(currow, curcol));
  1077.                linelim = strlen (line);
  1078.                insert_mode();
  1079.             }
  1080.             break;
  1081.         case 'e':
  1082.             if (!locked_cell(currow, curcol)) {
  1083.                editv (currow, curcol);
  1084.                edit_mode();
  1085.             }
  1086.             break;
  1087.         case 'E':
  1088.             if (!locked_cell(currow, curcol)) {
  1089.                edits (currow, curcol);
  1090.                edit_mode();
  1091.             }
  1092.             break;
  1093.         case 'f':
  1094.             if (arg == 1)
  1095.             (void) sprintf (line, "format [for column] %s ",
  1096.                 coltoa(curcol));
  1097.             else {
  1098.             (void) sprintf(line, "format [for columns] %s:",
  1099.                 coltoa(curcol));
  1100.             (void) sprintf(line+strlen(line), "%s ",
  1101.                 coltoa(curcol+arg-1));
  1102.             }
  1103.             error("Current format is %d %d %d",
  1104.             fwidth[curcol],precision[curcol],realfmt[curcol]);
  1105.             linelim = strlen (line);
  1106.             insert_mode();
  1107.             break;
  1108.         case 'F': {
  1109.             register struct ent *p = *ATBL(tbl, currow, curcol);
  1110.             if (p && p->format)
  1111.             {    (void) sprintf(line, "fmt [format] %s \"%s",
  1112.                    v_name(currow, curcol), p->format);
  1113.             edit_mode();
  1114.             }
  1115.             else
  1116.             {    (void) sprintf(line, "fmt [format] %s \"",
  1117.                    v_name(currow, curcol));
  1118.             insert_mode();
  1119.             }
  1120.             linelim = strlen(line);
  1121.             break;
  1122.         }
  1123.         case 'g':
  1124.             (void) sprintf (line, "goto [v] ");
  1125.             linelim = strlen (line);
  1126.             insert_mode();
  1127.             break;
  1128.         case 'P':
  1129.             (void) sprintf (line, "put [\"dest\" range] \"");
  1130.             if (*curfile)
  1131.             error ("Default path is \"%s\"",curfile);
  1132.             linelim = strlen (line);
  1133.             insert_mode();
  1134.             break;
  1135.         case 'M':
  1136.             (void) sprintf (line, "merge [\"source\"] \"");
  1137.             linelim = strlen (line);
  1138.             insert_mode();
  1139.             break;
  1140.         case 'R':
  1141.             if (mdir)
  1142.             (void) sprintf (line,"merge [\"macro_file\"] \"%s/", mdir);
  1143.             else
  1144.             (void) sprintf (line,"merge [\"macro_file\"] \"");
  1145.             linelim = strlen (line);
  1146.             insert_mode();
  1147.             break;
  1148.         case 'D':
  1149.             (void) sprintf (line, "mdir [\"macro_directory\"] \"");
  1150.             linelim = strlen (line);
  1151.             insert_mode();
  1152.             break;
  1153.         case 'G':
  1154.             (void) sprintf (line, "get [\"source\"] \"");
  1155.             if (*curfile)
  1156.             error ("Default file is \"%s\"",curfile);
  1157.             linelim = strlen (line);
  1158.             insert_mode();
  1159.             break;
  1160.         case 'W':
  1161.             (void) sprintf (line, "write [\"dest\" range] \"");
  1162.             if (*curfile)
  1163.                        default_file_is(curfile, ".asc");
  1164.             linelim = strlen (line);
  1165.             insert_mode();
  1166.             break;
  1167.         case 'S':    /* set options */
  1168.             (void) sprintf (line, "set ");
  1169.             error("Options:byrows,bycols,iterations=n,tblstyle=(0|tbl|latex|slatex|tex|frame),<MORE>");
  1170.             linelim = strlen (line);
  1171.             insert_mode();
  1172.             break;
  1173.         case 'T':    /* tbl output */
  1174.             (void) sprintf (line, "tbl [\"dest\" range] \"");
  1175.             if (*curfile && tbl_style == 0)
  1176.                        default_file_is(curfile, ".cln");
  1177.                     else if (*curfile && tbl_style == TBL)
  1178.                        default_file_is(curfile, ".tbl");
  1179.                     else if (*curfile && tbl_style == LATEX)
  1180.                        default_file_is(curfile, ".lat");
  1181.                     else if (*curfile && tbl_style == SLATEX)
  1182.                        default_file_is(curfile, ".stx");
  1183.                     else if (*curfile && tbl_style == TEX)
  1184.                        default_file_is(curfile, ".tex");
  1185.             linelim = strlen (line);
  1186.             insert_mode();
  1187.             break;
  1188.         case 'x':
  1189.             {
  1190.             register struct ent **pp;
  1191.             register int c1;
  1192.  
  1193.             flush_saved();
  1194.             if(calc_order == BYROWS) {
  1195.               for (c1 = curcol; arg-- && c1 < maxcols; c1++) {
  1196.             pp = ATBL(tbl, currow, c1);
  1197.                 if ((*pp) && !locked_cell(currow, curcol)) {
  1198.                if (*pp) {
  1199.                    free_ent(*pp);
  1200.                    *pp = (struct ent *)0;
  1201.                }
  1202.                 }
  1203.               }
  1204.             }
  1205.             else {
  1206.               for (c1 = currow; arg-- && c1 < maxrows; c1++) {
  1207.             pp = ATBL(tbl, c1, curcol);
  1208.                 if ((*pp) && !locked_cell(currow, curcol)) {
  1209.                if (*pp) {
  1210.                    free_ent(*pp);
  1211.                    *pp = (struct ent *)0;
  1212.                }
  1213.                 }
  1214.               }
  1215.             }
  1216.             sync_refs();
  1217.             modflg++;
  1218.             FullUpdate++;
  1219.             }
  1220.             break;
  1221.         case 'Q':
  1222.         case 'q':
  1223.             running = 0;
  1224.             break;
  1225.         case 'h':
  1226.             backcol(arg);
  1227.             break;
  1228.         case 'j':
  1229.             forwrow(arg);
  1230.             break;
  1231.         case 'k':
  1232.             backrow(arg);
  1233.             break;
  1234.         case 'H':
  1235.             backcol((curcol-stcol+1)+1);
  1236.             break;
  1237. #ifdef KEY_NPAGE
  1238.         case KEY_NPAGE:            /* page precedente */
  1239. #endif
  1240.         case 'J':
  1241.             forwrow(LINES-RESROW-(currow-strow)+1);
  1242.             break;
  1243. #ifdef    KEY_PPAGE
  1244.         case KEY_PPAGE:            /* page suivante */
  1245. #endif
  1246.         case 'K':
  1247.             backrow((currow-strow+1)+3);
  1248.             break;
  1249. #ifdef KEY_HOME
  1250.         case KEY_HOME:
  1251.             currow = 0;
  1252.             curcol = 0;
  1253.             FullUpdate++;
  1254.             break;
  1255. #endif
  1256.         case 'L':
  1257.             forwcol(lcols -(curcol-stcol)+1);
  1258.             break;
  1259.         case ' ':
  1260.         case 'l':
  1261.             forwcol(arg);
  1262.             break;
  1263.         case 'm':
  1264.             savedrow = currow;
  1265.             savedcol = curcol;
  1266.             break;
  1267.         case 'c': {
  1268.             register struct ent *p = *ATBL(tbl, savedrow, savedcol);
  1269.             register c1;
  1270.             register struct ent *n;
  1271.             if (!p)
  1272.             break;
  1273.             FullUpdate++;
  1274.             modflg++;
  1275.             for (c1 = curcol; arg-- && c1 < maxcols; c1++) {
  1276.             n = lookat (currow, c1);
  1277.             (void) clearent(n);
  1278.             copyent( n, p, currow - savedrow, c1 - savedcol);
  1279.             }
  1280.             break;
  1281.         }
  1282.         default:
  1283.             if ((toascii(c)) != c)
  1284.             error ("Weird character, decimal %d\n",
  1285.                 (int) c);
  1286.             else
  1287.                 error ("No such command (%c)", c);
  1288.             break;
  1289.         }
  1290.     edistate = nedistate;
  1291.     arg = narg;
  1292.     }                /* while (running) */
  1293.     inloop = modcheck(" before exiting");
  1294.     }                /*  while (inloop) */
  1295.     stopdisp();
  1296. #ifdef VMS    /* Until VMS "fixes" exit we should say 1 here */
  1297.     exit(1);
  1298. #else
  1299.     exit(0);
  1300. #endif
  1301.     /*NOTREACHED*/
  1302. }
  1303.  
  1304. /* show the current range (see ^I), we are moving around to define a range */
  1305. void
  1306. startshow()
  1307. {
  1308.     showrange = 1;
  1309.     showsr = currow;
  1310.     showsc = curcol;
  1311. }
  1312.  
  1313. /* insert the range we defined by moving around the screen, see startshow() */
  1314. void
  1315. showdr()
  1316. {
  1317.     int     minsr, minsc, maxsr, maxsc;
  1318.  
  1319.     minsr = showsr < currow ? showsr : currow;
  1320.     minsc = showsc < curcol ? showsc : curcol;
  1321.     maxsr = showsr > currow ? showsr : currow;
  1322.     maxsc = showsc > curcol ? showsc : curcol;
  1323.     (void) sprintf (line+linelim,"%s", r_name(minsr, minsc, maxsr, maxsc));
  1324. }
  1325.  
  1326. /* set the calculation order */
  1327. void
  1328. setorder(i)
  1329. int i;
  1330. {
  1331.     if((i == BYROWS)||(i == BYCOLS))
  1332.         calc_order = i;
  1333. }
  1334.  
  1335. void
  1336. setauto(i)
  1337. int i;
  1338. {
  1339.     autocalc = i;
  1340. }
  1341.  
  1342. void
  1343. signals()
  1344. {
  1345. #ifdef SIGVOID
  1346.     void doquit();
  1347.     void time_out();
  1348.     void dump_me();
  1349. #ifdef    SIGWINCH
  1350.     void winchg();
  1351. #endif
  1352. #else
  1353.     int doquit();
  1354.     int time_out();
  1355.     int dump_me();
  1356. #ifdef    SIGWINCH
  1357.     int winchg();
  1358. #endif
  1359. #endif
  1360.  
  1361.     (void) signal(SIGINT, SIG_IGN);
  1362. #if !defined(MSDOS) && !defined(__OS2__)
  1363.     (void) signal(SIGQUIT, dump_me);
  1364.     (void) signal(SIGPIPE, doquit);
  1365.     (void) signal(SIGALRM, time_out);
  1366.     (void) signal(SIGBUS, doquit);
  1367. #endif
  1368.     (void) signal(SIGTERM, doquit);
  1369.     (void) signal(SIGFPE, doquit);
  1370. #ifdef    SIGWINCH
  1371.     (void) signal(SIGWINCH, winchg);
  1372. #endif
  1373. }
  1374.  
  1375. #ifdef    SIGWINCH
  1376. #ifdef SIGVOID
  1377. void
  1378. #else
  1379. int
  1380. #endif
  1381. winchg()
  1382. {    hitwinch++;
  1383.     (void) signal(SIGWINCH, winchg);
  1384. }
  1385. #endif
  1386.  
  1387. #ifdef SIGVOID
  1388. void
  1389. #else
  1390. int
  1391. #endif
  1392. doquit()
  1393. {
  1394.     diesave();
  1395.     stopdisp();
  1396.     exit(1);
  1397. }
  1398.  
  1399. #ifdef SIGVOID
  1400. void
  1401. #else
  1402. int
  1403. #endif
  1404. dump_me()
  1405. {
  1406.     diesave();
  1407.     deraw();
  1408.     abort();
  1409. }
  1410.  
  1411. /* try to save the current spreadsheet if we can */
  1412. void
  1413. diesave()
  1414. {   char    path[PATHLEN];
  1415.  
  1416.     if (modcheck(" before Spreadsheet dies") == 1)
  1417.     {
  1418. #if defined(MSDOS) || defined(__OS2__)
  1419.     (void) sprintf(path, "%s", SAVENAME);
  1420. #else
  1421.         (void) sprintf(path, "~/%s", SAVENAME);
  1422. #endif
  1423.     if (writefile(path, 0, 0, maxrow, maxcol) < 0)
  1424.     {
  1425.         (void) sprintf(path, "/tmp/%s", SAVENAME);
  1426.         if (writefile(path, 0, 0, maxrow, maxcol) < 0)
  1427.         error("Couldn't save current spreadsheet, Sorry");
  1428.     }
  1429.     }
  1430. }
  1431.  
  1432. /* check if tbl was modified and ask to save */
  1433. int
  1434. modcheck(endstr)
  1435. char *endstr;
  1436. {
  1437.     if (modflg && curfile[0]) {
  1438.     int    yn_ans;
  1439.     char    lin[100];
  1440.  
  1441.     (void) sprintf (lin,"File \"%s\" is modified, save%s? ",curfile,endstr);
  1442.     if ((yn_ans = yn_ask(lin)) < 0)
  1443.         return(1);
  1444.     else
  1445.     if (yn_ans == 1)
  1446.     {    if (writefile(curfile, 0, 0, maxrow, maxcol) < 0)
  1447.          return (1);
  1448.     }
  1449.     } else if (modflg) {
  1450.     int    yn_ans;
  1451.  
  1452.     if ((yn_ans = yn_ask("Do you want a chance to save the data? ")) < 0)
  1453.         return(1);
  1454.     else
  1455.         return(yn_ans);
  1456.     }
  1457.     return(0);
  1458. }
  1459.  
  1460. /* Returns 1 if cell is locked, 0 otherwise */
  1461. int
  1462. locked_cell (r, c)
  1463.     int r, c;
  1464. {
  1465.     struct ent *p = *ATBL(tbl, r, c);
  1466.     if (p && (p->flags & is_locked)) {
  1467.         error("Cell %s%d is locked", coltoa(c), r) ;
  1468.         return(1);
  1469.     }
  1470.     return(0);
  1471. }
  1472.  
  1473. /* Check if area contains locked cells */
  1474. int
  1475. any_locked_cells(r1, c1, r2, c2)
  1476.     int r1, c1, r2, c2 ;
  1477. {
  1478.     int r, c;
  1479.     struct ent *p ;
  1480.  
  1481.     for (r=r1; r<=r2; r++)
  1482.         for (c=c1; c<=c2; c++) {
  1483.             p = *ATBL(tbl, r, c);
  1484.             if (p && (p->flags&is_locked))
  1485.                 return(1);
  1486.         }
  1487.     return(0);
  1488. }
  1489.