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