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