/* SC A Spreadsheet Calculator * Main driver * * original by James Gosling, September 1982 * modifications by Mark Weiser and Bruce Israel, * University of Maryland * * More mods Robert Bond, 12/86 * More mods by Alan Silverstein, 3-4/88, see list of changes. * Currently supported by pur-phy!sawmill!buhrt (Jeff Buhrt) * $Revision: 6.8 $ * */ #include #include #include #ifdef BSD42 #include #else #ifndef SYSIII #include #endif #endif #include #include "sc.h" char *getenv(); #ifdef SYSV3 void exit(); #endif #ifndef DFLT_PAGER #define DFLT_PAGER "more" /* more is probably more widespread than less */ #endif /* DFLT_PAGER */ #define MAXCMD 160 /* for ! command below */ extern char *rev; /* Globals defined in sc.h */ struct ent ***tbl; int strow, stcol; int currow, curcol; int savedrow, savedcol; int FullUpdate; int maxrow, maxcol; int maxrows, maxcols; int *fwidth; int *precision; char *col_hidden; char *row_hidden; char line[FBUFLEN]; int changed; struct ent *to_fix; int modflg; int numeric; char *mdir; int showsc, showsr; /* Starting cell for highlighted range */ char mode_ind = '.'; char curfile[PATHLEN]; char revmsg[80]; int linelim = -1; int showtop = 1; /* Causes current cell value display in top line */ int showcell = 1; /* Causes current cell to be highlighted */ int showrange = 0; /* Causes ranges to be highlighted */ int showneed = 0; /* Causes cells needing values to be highlighted */ int showexpr = 0; /* Causes cell exprs to be displayed, highlighted */ int autocalc = 1 ; /* 1 to calculate after each update */ int calc_order = BYROWS; int tbl_style = 0; /* headers for T command output */ int lastmx, lastmy; /* Screen address of the cursor */ int lastcol; /* Spreadsheet Column the cursor was in last */ char under_cursor[] = " "; /* Data under the < cursor */ #ifdef VMS int VMS_read_raw = 0; #endif int seenerr; void yyerror(err) char *err; { if (seenerr) return; seenerr++; (void) move(1,0); (void) clrtoeol(); (void) printw("%s: %.*s<=%s",err,linelim,line,line+linelim); } struct ent * lookat(row,col) int row, col; { register struct ent **pp; checkbounds(&row, &col); pp = ATBL(tbl, row, col); if (*pp == (struct ent *)0) { *pp = (struct ent *) xmalloc((unsigned)sizeof(struct ent)); if (row>maxrow) maxrow = row; if (col>maxcol) maxcol = col; (*pp)->label = (char *)0; (*pp)->row = row; (*pp)->col = col; (*pp)->flags = 0; (*pp)->expr = (struct enode *)0; (*pp)->v = (double) 0.0; (*pp)->evnext = (struct ent *)0; } return *pp; } /* * This structure is used to keep ent structs around before they * are deleted to allow the sync_refs routine a chance to fix the * variable references. * We also use it as a last-deleted buffer for the 'p' command. */ void free_ent(p) register struct ent *p; { p->next = to_fix; to_fix = p; p->flags |= is_deleted; } void flush_saved() { register struct ent *p; register struct ent *q; if (!(p = to_fix)) return; while (p) { (void) clearent(p); q = p->next; xfree((char *)p); p = q; } to_fix = 0; } /* * standout last time in update()? * At this point we will let curses do work */ int standlast = FALSE; void update (anychanged) int anychanged; /* did any cell really change in value? */ { register row, col; register struct ent **pp; int mxcol; int mxrow; int rows; int cols; int minsr, minsc, maxsr, maxsc; register r; register i; while (row_hidden[currow]) /* You can't hide the last row or col */ currow++; while (col_hidden[curcol]) curcol++; /* First see if the last display still covers curcol */ if (stcol <= curcol) { for (i = stcol, cols = 0, col = RESCOL; (col + fwidth[i]) < COLS-1 && i < maxcols; i++) { cols++; if (col_hidden[i]) continue; col += fwidth[i]; } } while (stcol + cols - 1 < curcol || curcol < stcol) { FullUpdate++; if (stcol - 1 == curcol) { /* How about back one? */ stcol--; } else if (stcol + cols == curcol) { /* Forward one? */ stcol++; } else { /* Try to put the cursor in the center of the screen */ col = (COLS - RESCOL - fwidth[curcol]) / 2 + RESCOL; stcol = curcol; for (i=curcol-1; i >= 0 && col-fwidth[i] > RESCOL; i--) { stcol--; if (col_hidden[i]) continue; col -= fwidth[i]; } } /* Now pick up the counts again */ for (i = stcol, cols = 0, col = RESCOL; (col + fwidth[i]) < COLS-1 && i < maxcols; i++) { cols++; if (col_hidden[i]) continue; col += fwidth[i]; } } /* Now - same process on the rows */ if (strow <= currow) { for (i = strow, rows = 0, row=RESROW; row= 0 && row-1 > RESROW; i--) { strow--; if (row_hidden[i]) continue; row--; } } /* Now pick up the counts again */ for (i = strow, rows = 0, row=RESROW; row currow ? showsr : currow; maxsc = showsc > curcol ? showsc : curcol; if (showtop) { (void) move(1,0); (void) clrtoeol(); (void) printw("Default range: %s", r_name(minsr, minsc, maxsr, maxsc)); } } /* Repaint the visible screen */ if (showrange || anychanged || FullUpdate || standlast) { /* may be reset in loop, if not next time we will do a FullUpdate */ if (standlast) { FullUpdate = TRUE; standlast = FALSE; } for (row = strow, r = RESROW; row <= mxrow; row++) { register c = RESCOL; int do_stand = 0; int fieldlen; int nextcol; if (row_hidden[row]) continue; for (pp = ATBL(tbl, row, col = stcol); col <= mxcol; pp += nextcol - col, col = nextcol, c += fieldlen) { nextcol = col+1; if (col_hidden[col]) { fieldlen = 0; continue; } fieldlen = fwidth[col]; /* * Set standout if: * * - showing ranges, and not showing cells which need to be filled * in, and not showing cell expressions, and in a range, OR * * - if showing cells which need to be filled in and this one is * of that type (has a value and doesn't have an expression, * or it is a string expression), OR * * - if showing cells which have expressions and this one does. */ if ((showrange && (! showneed) && (! showexpr) && (row >= minsr) && (row <= maxsr) && (col >= minsc) && (col <= maxsc)) || (showneed && (*pp) && ((*pp) -> flags & is_valid) && (((*pp) -> flags & is_strexpr) || !((*pp) -> expr))) || (showexpr && (*pp) && ((*pp) -> expr))) { (void) move(r, c); (void) standout(); standlast++; if (!*pp) /* no cell, but standing out */ { (void) printw("%*s", fwidth[col], " "); (void) standend(); continue; } else do_stand = 1; } else do_stand = 0; if ((*pp) && ((*pp) -> flags & is_changed || FullUpdate) || do_stand) { if (do_stand) { (*pp) -> flags |= is_changed; } else { (void) move(r, c); (*pp) -> flags &= ~is_changed; } /* * Show expression; takes priority over other displays: */ if (showexpr && ((*pp) -> expr)) { linelim = 0; editexp(row, col); /* set line to expr */ linelim = -1; showstring(line, /* leftflush = */ 1, /* hasvalue = */ 0, row, col, & nextcol, mxcol, & fieldlen, r, c); } else { /* * Show cell's numeric value: */ if ((*pp) -> flags & is_valid) { char field[FBUFLEN]; (void)sprintf(field,"%*.*f", fwidth[col], precision[col], (*pp)->v); if(strlen(field) > fwidth[col]) { for(i = 0; i label) { showstring((*pp) -> label, (*pp) -> flags & is_leftflush, (*pp) -> flags & is_valid, row, col, & nextcol, mxcol, & fieldlen, r, c); } else /* repaint a blank cell: */ if ((do_stand || !FullUpdate) && ((*pp)->flags & is_changed) && !((*pp)->flags & is_valid) && !(*pp)->label) { (void) printw("%*s", fwidth[col], " "); } } /* else */ if (do_stand) { (void) standend(); do_stand = 0; } } } r++; } } (void) move(lastmy, lastmx+fwidth[lastcol]); if((inch() & A_CHARTEXT ) == '<') (void) addstr(under_cursor); lastmy = RESROW; for (row = strow; row < currow; row++) if (!row_hidden[row]) lastmy += 1; lastmx = RESCOL; for (col = stcol; col < curcol; col++) if (!col_hidden[col]) lastmx += fwidth[col]; lastcol = curcol; if (showcell && (! showneed) && (! showexpr)) { (void) move(lastmy, lastmx); (void) standout(); repaint(lastmx, lastmy, fwidth[lastcol]); (void) standend(); } (void) move(lastmy, lastmx+fwidth[lastcol]); *under_cursor = (inch() & A_CHARTEXT ); (void) addstr("<"); (void) move(0, 0); (void) clrtoeol(); if (linelim >= 0) { (void) addch(mode_ind); (void) addstr("> "); (void) addstr(line); (void) move(0, linelim+3); } else { if (showtop) { /* show top line */ register struct ent *p1; int printed = 0; /* printed something? */ (void) printw("%s%d ", coltoa(curcol), currow); if (p1 = *ATBL(tbl, currow, curcol)) { if (p1 -> expr) { /* has expr of some type */ linelim = 0; editexp(currow, curcol); /* set line to expr */ linelim = -1; } /* * Display string part of cell: */ if ((p1 -> expr) && (p1 -> flags & is_strexpr)) { (void) addstr((p1 -> flags & is_leftflush) ? "<{" : ">{"); (void) addstr(line); (void) addstr("} "); /* and this '}' is for vi % */ printed = 1; } else if (p1 -> label) { /* has constant label only */ (void) addstr ((p1 -> flags & is_leftflush) ? "<\"" : ">\""); (void) addstr (p1 -> label); (void) addstr ("\" "); printed = 1; } /* * Display value part of cell: */ if (p1 -> flags & is_valid) { /* has value or num expr */ if ((! (p1 -> expr)) || (p1 -> flags & is_strexpr)) (void) sprintf (line, "%.15g", p1 -> v); (void) addstr ("["); (void) addstr (line); (void) addstr ("]"); printed = 1; } } if (! printed) (void) addstr ("[]"); } (void) move (lastmy, lastmx + fwidth[lastcol]); } if (revmsg[0]) { (void) move(0, 0); (void) clrtoeol (); /* get rid of topline display */ (void) printw(revmsg); revmsg[0] = '\0'; /* don't show it again */ (void) move (lastmy, lastmx + fwidth[lastcol]); } FullUpdate = FALSE; } void repaint(x, y, len) int x, y, len; { int c; while(len-- > 0) { (void) move(y,x); c = inch() & A_CHARTEXT; (void) addch(c); x++; } } char *progname; int main (argc, argv) int argc; char **argv; { int inloop = 1; register int c; int edistate = -1; int arg = 1; int narg; int nedistate; int running; char *revi; int anychanged = FALSE; /* * Keep command line options around until the file is read so the * command line overrides file options */ int Mopt = 0; int Nopt = 0; int Copt = 0; int Ropt = 0; int tempx, tempy; /* Temp versions of curx, cury */ if ((revi = strrchr(argv[0], '/')) != NULL) progname = revi+1; else progname = argv[0]; while (argc > 1 && argv[1][0] == '-') { argv++; argc--; switch (argv[0][1]) { case 'x': #ifdef VMS (void) fprintf(stderr, "Crypt not available for VMS\n"); exit(1); #else Crypt = 1; #endif break; case 'm': Mopt = 1; break; case 'n': Nopt = 1; break; case 'c': Copt = 1; break; case 'r': Ropt = 1; break; default: (void) fprintf(stderr,"%s: unrecognized option: \"%c\"\n", progname,argv[0][1]); exit(1); } } *curfile ='\0'; signals(); (void) initscr(); /* setup the spreadsheet arrays, initscr() will get the screen size */ if (!growtbl(GROWNEW, 0, 0)) { endwin(); exit(1); } (void) clear(); #ifdef VMS VMS_read_raw = 1; #else nonl(); noecho (); cbreak(); #endif initkbd(); scrollok(stdscr, 1); /* * Build revision message for later use: */ (void) strcpy (revmsg, progname); for (revi = rev; (*revi++) != ':'; ); /* copy after colon */ (void) strcat (revmsg, revi); revmsg [strlen (revmsg) - 2] = 0; /* erase last character */ (void) strcat (revmsg, ": Type '?' for help."); if (argc > 1) { (void) strcpy(curfile,argv[1]); readfile (argv[1], 0); } if (Mopt) autocalc = 0; if (Nopt) numeric = 1; if (Copt) calc_order = BYCOLS; if (Ropt) calc_order = BYROWS; modflg = 0; #ifdef VENIX setbuf (stdin, NULL); #endif FullUpdate++; while (inloop) { running = 1; while (running) { nedistate = -1; narg = 1; if (edistate < 0 && linelim < 0 && autocalc && (changed || FullUpdate)) { EvalAll (); if (changed) /* if EvalAll changed or was before */ anychanged = TRUE; changed = 0; } else /* any cells change? */ if (changed) anychanged = TRUE; update(anychanged); anychanged = FALSE; #ifndef SYSV3 (void) refresh(); /* 5.3 does a refresh in getch */ #endif c = nmgetch(); getyx(stdscr, tempy, tempx); (void) move (1, 0); (void) clrtoeol (); (void) move(tempy, tempx); (void) fflush (stdout); seenerr = 0; showneed = 0; /* reset after each update */ showexpr = 0; /* if ((c < ' ') || ( c == DEL )) how about international here ? PB */ if ( iscntrl(c) ) switch (c) { #ifdef SIGTSTP case ctl('z'): (void) deraw(); (void) kill(0, SIGTSTP); /* Nail process group */ /* the pc stops here */ (void) goraw(); break; #endif case ctl('r'): showneed = 1; case ctl('l'): FullUpdate++; (void) clearok(stdscr,1); break; case ctl('x'): FullUpdate++; showexpr = 1; (void) clearok(stdscr,1); break; default: error ("No such command (^%c)", c + 0100); break; case ctl('b'): backcol(arg); break; case ctl('c'): running = 0; break; case ctl('e'): switch (nmgetch()) { case ctl('p'): case 'k': doend (-1, 0); break; case ctl('n'): case 'j': doend ( 1, 0); break; case ctl('b'): case 'h': case ctl('h'): doend ( 0,-1); break; case ctl('f'): case 'l': case ctl('i'): case ' ': doend ( 0, 1); break; case ESC: case ctl('g'): break; default: error("Invalid ^E command"); break; } break; case ctl('f'): forwcol(arg); break; case ctl('g'): showrange = 0; linelim = -1; (void) move (1, 0); (void) clrtoeol (); break; case ESC: /* ctl('[') */ write_line(ESC); break; case ctl('d'): write_line(ctl('d')); break; case DEL: case ctl('h'): if (linelim < 0) { /* not editing line */ backcol(arg); /* treat like ^B */ break; } write_line(ctl('h')); break; case ctl('i'): /* tab */ if (linelim < 0) { /* not editing line */ forwcol(arg); break; } if (!showrange) { startshow(); } else { showdr(); linelim = strlen(line); line[linelim++] = ' '; line[linelim] = 0; showrange = 0; } linelim = strlen (line); break; case ctl('m'): case ctl('j'): write_line(ctl('m')); break; case ctl('n'): forwrow(arg); break; case ctl('p'): backrow(arg); break; case ctl('q'): break; /* ignore flow control */ case ctl('s'): break; /* ignore flow control */ case ctl('t'): error( "Toggle: a:auto c:cell e:ext funcs n:numeric t:top x:encrypt $:pre-scale"); (void) refresh(); switch (nmgetch()) { case 'a': case 'A': case 'm': case 'M': autocalc ^= 1; error("Automatic recalculation %sabled.", autocalc ? "en":"dis"); break; case 'n': case 'N': numeric = (! numeric); error ("Numeric input %sabled.", numeric ? "en" : "dis"); break; case 't': case 'T': showtop = (! showtop); repaint(lastmx, lastmy, fwidth[lastcol]); error ("Top line %sabled.", showtop ? "en" : "dis"); break; case 'c': case 'C': showcell = (! showcell); repaint(lastmx, lastmy, fwidth[lastcol]); error ("Cell highlighting %sabled.", showcell ? "en" : "dis"); break; case 'x': case 'X': Crypt = (! Crypt); error ("Encryption %sabled.", Crypt? "en" : "dis"); break; case '$': if (prescale == 1.0) { error ("Prescale enabled."); prescale = 0.01; } else { prescale = 1.0; error ("Prescale disabled."); } break; case 'e': case 'E': extfunc = (! extfunc); error ("External functions %sabled.", extfunc? "en" : "dis"); break; case ESC: case ctl('g'): --modflg; /* negate the modflg++ */ break; default: error ("Invalid toggle command"); --modflg; /* negate the modflg++ */ } FullUpdate++; modflg++; break; case ctl('u'): narg = arg * 4; nedistate = 1; break; case ctl('v'): /* insert variable name */ if (linelim > 0) ins_string(v_name(currow, curcol)); break; case ctl('w'): /* insert variable expression */ if (linelim > 0) { char *temp, *temp1; int templim; temp = strcpy(xmalloc((unsigned)(strlen(line)+1)),line); templim = linelim; editexp(currow,curcol); temp1= strcpy(xmalloc((unsigned)(strlen(line)+1)),line); strcpy(line, temp); linelim = templim; ins_string(temp1); xfree(temp); xfree(temp1); } break; case ctl('a'): /* insert variable value */ if (linelim > 0) { struct ent *p = *ATBL(tbl, currow, curcol); char temp[100]; if (p && p -> flags & is_valid) { (void) sprintf (temp, "%.*f", precision[curcol],p -> v); ins_string(temp); } } break; } /* End of the control char switch stmt */ else if (isdigit(c) && ((numeric && edistate >= 0) || (!numeric && (linelim < 0 || edistate >= 0)))) { /* we got a leading number */ if (edistate != 0) { /* First char of the count */ if (c == '0') /* just a '0' goes to left col */ curcol = 0; else { nedistate = 0; narg = c - '0'; } } else { /* Succeeding count chars */ nedistate = 0; narg = arg * 10 + (c - '0'); } } else if (linelim >= 0) { /* Editing line */ switch(c) { case ')': if (showrange) { showdr(); showrange = 0; linelim = strlen (line); } break; default: break; } write_line(c); } else if (!numeric && ( c == '+' || c == '-' ) ) { /* increment/decrement ops */ register struct ent *p = *ATBL(tbl, currow, curcol); if (!p) continue; if (p->expr && !(p->flags & is_strexpr)) { error("Can't increment/decrement a formula\n"); continue; } FullUpdate++; modflg++; if( c == '+' ) p -> v += (double) arg; else p -> v -= (double) arg; } else /* switch on a normal command character */ switch (c) { case ':': break; /* Be nice to vi users */ case '@': EvalAll (); changed = 0; anychanged = TRUE; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '.': case '+': (void) sprintf(line,"let %s = %c", v_name(currow, curcol), c); linelim = strlen (line); insert_mode(); break; case '=': (void) sprintf(line,"let %s = ", v_name(currow, curcol)); linelim = strlen (line); insert_mode(); break; case '!': { /* * "! command" executes command * "!" forks a shell * "!!" repeats last command */ #ifdef VMS error("Not implemented on VMS"); #else /* VMS */ char *shl; int pid, temp; char cmd[MAXCMD]; static char lastcmd[MAXCMD]; if (!(shl = getenv("SHELL"))) shl = "/bin/sh"; deraw(); (void) fputs("! ", stdout); (void) fflush(stdout); (void) fgets(cmd, MAXCMD, stdin); cmd[strlen(cmd) - 1] = '\0'; /* clobber \n */ if(strcmp(cmd,"!") == 0) /* repeat? */ (void) strcpy(cmd, lastcmd); else (void) strcpy(lastcmd, cmd); if (modflg) { (void) puts ("[No write since last change]"); (void) fflush (stdout); } if (!(pid = fork())) { (void) signal (SIGINT, SIG_DFL); /* reset */ if(strlen(cmd)) (void)execl(shl,shl,"-c",cmd,(char *)0); else (void) execl(shl, shl, (char *)0); exit(-127); } while (pid != wait(&temp)); (void) printf("Press RETURN to continue "); (void)nmgetch(); goraw(); #endif /* VMS */ break; } /* * Range commands: */ case '/': error ( "Range: x:erase v:value c:copy f:fill d:define s:show u:undefine"); (void) refresh(); switch (nmgetch()) { case 'c': (void) sprintf(line,"copy [dest_range src_range] "); linelim = strlen(line); startshow(); insert_mode(); break; case 'x': (void) sprintf(line,"erase [range] "); linelim = strlen(line); startshow(); insert_mode(); break; case 'v': (void) sprintf(line, "value [range] "); linelim = strlen(line); startshow(); insert_mode(); break; case 'f': (void) sprintf(line,"fill [range start inc] "); linelim = strlen(line); startshow(); insert_mode(); break; case 'd': (void) sprintf(line,"define [string range] \""); linelim = strlen(line); startshow(); insert_mode(); modflg++; break; case 'u': (void) sprintf(line,"undefine [range] "); linelim = strlen(line); insert_mode(); modflg++; break; case 's': if(are_ranges()) { FILE *f; int pid; char px[MAXCMD] ; char *pager; (void) strcpy(px, "| sort | "); if(!(pager = getenv("PAGER"))) pager = DFLT_PAGER; (void) strcat(px,pager); f = openout(px, &pid); if (!f) { error("Can't open pipe to sort"); break; } list_range(f); closeout(f, pid); } else error("No ranges defined"); break; case ESC: case ctl('g'): break; default: error("Invalid region command"); break; } break; /* * Row/column commands: */ case 'i': case 'a': case 'd': case 'p': case 'v': case 'z': case 's': { register rcqual; if (! (rcqual = get_rcqual (c))) { error ("Invalid row/column command"); break; } error (""); /* clear line */ if ( rcqual == ESC || rcqual == ctl('g')) break; switch (c) { case 'i': if (rcqual == 'r') insertrow(arg); else opencol(curcol, arg); break; case 'a': if (rcqual == 'r') while (arg--) duprow(); else while (arg--) dupcol(); break; case 'd': if (rcqual == 'r') deleterow(arg); else closecol(curcol, arg); break; case 'p': while (arg--) pullcells(rcqual); break; case 'v': if (rcqual == 'r') rowvalueize(arg); else colvalueize(arg); modflg = 1; break; case 'z': if (rcqual == 'r') hiderow(arg); else hidecol(arg); break; case 's': /* special case; no repeat count */ if (rcqual == 'r') rowshow_op(); else colshow_op(); break; } break; } case '$': { register struct ent *p; curcol = maxcols - 1; while (!VALID_CELL(p, currow, curcol) && curcol > 0) curcol--; break; } case '#': { register struct ent *p; currow = maxrows - 1; while (!VALID_CELL(p, currow, curcol) && currow > 0) currow--; break; } case 'w': { register struct ent *p; while (--arg>=0) { do { if (curcol < maxcols - 1) curcol++; else { if (currow < maxrows - 1) { while(++currow < maxrows - 1 && row_hidden[currow]) /* */; curcol = 0; } else { error("At end of table"); break; } } } while(col_hidden[curcol] || !VALID_CELL(p, currow, curcol)); } break; } case 'b': { register struct ent *p; while (--arg>=0) { do { if (curcol) curcol--; else { if (currow) { while(--currow && row_hidden[currow]) /* */; curcol = maxcols - 1; } else { error ("At start of table"); break; } } } while(col_hidden[curcol] || !VALID_CELL(p, currow, curcol)); } break; } case '^': currow = 0; break; case '?': help(); break; case '"': (void) sprintf (line, "label %s = \"", v_name(currow, curcol)); linelim = strlen (line); insert_mode(); break; case '<': (void) sprintf (line, "leftstring %s = \"", v_name(currow, curcol)); linelim = strlen (line); insert_mode(); break; case '>': (void) sprintf (line, "rightstring %s = \"", v_name(currow, curcol)); linelim = strlen (line); insert_mode(); break; case 'e': editv (currow, curcol); edit_mode(); break; case 'E': edits (currow, curcol); edit_mode(); break; case 'f': if (arg == 1) (void) sprintf (line, "format [for column] %s ", coltoa(curcol)); else { (void) sprintf(line, "format [for columns] %s:", coltoa(curcol)); (void) sprintf(line+strlen(line), "%s ", coltoa(curcol+arg-1)); } error("Current format is %d %d", fwidth[curcol],precision[curcol]); linelim = strlen (line); insert_mode(); break; case 'g': (void) sprintf (line, "goto [v] "); linelim = strlen (line); insert_mode(); break; case 'P': (void) sprintf (line, "put [\"dest\" range] \""); if (*curfile) error ("Default path is \"%s\"",curfile); linelim = strlen (line); insert_mode(); break; case 'M': (void) sprintf (line, "merge [\"source\"] \""); linelim = strlen (line); insert_mode(); break; case 'R': if (mdir) (void) sprintf (line,"merge [\"macro_file\"] \"%s/", mdir); else (void) sprintf (line,"merge [\"macro_file\"] \""); linelim = strlen (line); insert_mode(); break; case 'D': (void) sprintf (line, "mdir [\"macro_directory\"] \""); linelim = strlen (line); insert_mode(); break; case 'G': (void) sprintf (line, "get [\"source\"] \""); if (*curfile) error ("Default file is \"%s\"",curfile); linelim = strlen (line); insert_mode(); break; case 'W': (void) sprintf (line, "write [\"dest\" range] \""); linelim = strlen (line); insert_mode(); break; case 'S': /* set options */ (void) sprintf (line, "set "); error("Options: byrows, bycols, iterations=n, tblstyle=(0|tbl|latex|tex)"); linelim = strlen (line); insert_mode(); break; case 'T': /* tbl output */ (void) sprintf (line, "tbl [\"dest\" range] \""); linelim = strlen (line); insert_mode(); break; case 'x': { register struct ent **pp; register int c1; flush_saved(); if(calc_order == BYROWS) { for (c1 = curcol; arg-- && c1 < maxcols; c1++) { pp = ATBL(tbl, currow, c1); if (*pp) { free_ent(*pp); *pp = (struct ent *)0; } } } else { for (c1 = currow; arg-- && c1 < maxrows; c1++) { pp = ATBL(tbl, c1, curcol); if (*pp) { free_ent(*pp); *pp = (struct ent *)0; } } } sync_refs(); modflg++; FullUpdate++; } break; case 'Q': case 'q': running = 0; break; case 'h': backcol(arg); break; case 'j': forwrow(arg); break; case 'k': backrow(arg); break; case ' ': case 'l': forwcol(arg); break; case 'm': savedrow = currow; savedcol = curcol; break; case 'c': { register struct ent *p = *ATBL(tbl, savedrow, savedcol); register c1; register struct ent *n; if (!p) break; FullUpdate++; modflg++; for (c1 = curcol; arg-- && c1 < maxcols; c1++) { n = lookat (currow, c1); (void) clearent(n); copyent( n, p, currow - savedrow, c1 - savedcol); } break; } default: if ((toascii(c)) != c) error ("Weird character, decimal %d\n", (int) c); else error ("No such command (%c)", c); break; } edistate = nedistate; arg = narg; } /* while (running) */ inloop = modcheck(" before exiting"); } /* while (inloop) */ deraw(); endwin(); #ifdef VMS /* Unit VMS "fixes" exit we should say 1 here */ exit(1); #else exit(0); #endif /*NOTREACHED*/ } void startshow() { showrange = 1; showsr = currow; showsc = curcol; } void showdr() { int minsr, minsc, maxsr, maxsc; minsr = showsr < currow ? showsr : currow; minsc = showsc < curcol ? showsc : curcol; maxsr = showsr > currow ? showsr : currow; maxsc = showsc > curcol ? showsc : curcol; (void) sprintf (line+linelim,"%s", r_name(minsr, minsc, maxsr, maxsc)); } void setorder(i) int i; { if((i == BYROWS)||(i == BYCOLS)) calc_order = i; else error("Not yet implemented"); } void setauto(i) int i; { autocalc = i; } #ifdef VMS goraw() { VMS_read_raw = 1; FullUpdate++; } deraw() { (void) move (LINES - 1, 0); (void) clrtoeol(); (void) refresh(); VMS_read_raw = 0; } #else /* VMS */ void goraw() { #if SYSV2 || SYSV3 fixterm(); #else /* SYSV2 || SYSV3 */ cbreak(); nonl(); noecho (); #endif /* SYSV2 || SYSV3 */ kbd_again(); (void) clear(); FullUpdate++; } void deraw() { (void) move (LINES - 1, 0); (void) clrtoeol(); (void) refresh(); #if SYSV2 || SYSV3 resetterm(); #else nocbreak(); nl(); echo(); #endif resetkbd(); } #endif /* VMS */ void signals() { #ifdef SIGVOID void quit(); void time_out(); void dump_me(); #else int quit(); int time_out(); int dump_me(); #endif (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, dump_me); (void) signal(SIGPIPE, quit); (void) signal(SIGTERM, quit); (void) signal(SIGALRM, time_out); (void) signal(SIGFPE, quit); (void) signal(SIGBUS, quit); } #ifdef SIGVOID void #endif quit() { diesave(); deraw(); resetkbd(); endwin(); exit(1); } #ifdef SIGVOID void #endif dump_me() { diesave(); deraw(); abort(); } /* try to save the current spreadsheet if we can */ diesave() { char path[PATHLEN]; if (modcheck(" before Spreadsheet dies") == 1) { sprintf(path, "~/SC.SAVE"); if (writefile(path, 0, 0, maxrow, maxcol) < 0) if (writefile("/tmp/SC.SAVE", 0, 0, maxrow, maxcol) < 0) error("Couldn't save current spreadsheet, Sorry"); } } int modcheck(endstr) char *endstr; { if (modflg && curfile[0]) { int yn_ans; char lin[100]; (void) sprintf (lin,"File \"%s\" is modified, save%s? ",curfile,endstr); if ((yn_ans = yn_ask(lin)) < 0) return(1); else if (yn_ans == 1) { if (writefile(curfile, 0, 0, maxrow, maxcol) < 0) return (1); } } else if (modflg) { int yn_ans; if ((yn_ans = yn_ask("Do you want a chance to save the data? ")) < 0) return(1); else return(yn_ans); } return(0); }