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