/* * This file is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify this file without charge, but are not authorized to * license or distribute it to anyone else except as part of a product * or program developed by the user. * * THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * This file is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS FILE * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even * if Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ /* * 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 * */ #include #include "scdisp.h" #ifdef BSD42 #include #else #ifndef SYSIII #include #endif #endif #include #include "sc.h" #if BSD42 || SYSIII #define cbreak crmode #define nocbreak nocrmode #endif char *malloc(); /* default column width */ #define DEFWIDTH (10*charwidth) #define DEFPREC 2 #define RESCOL (4*charwidth) /* columns reserved for row numbers */ #define RESROW 1 /* rows reserved for prompt, error, and column * numbers */ char curfile[1024]; int linelim = -1; int showme = 1; /* 1 to display the current cell in the top * line */ char *rev = "$Revision: 3.1 $"; int seenerr; yyerror(err) char *err; { if (seenerr) return; seenerr++; error("%s", err); } struct ent * lookat(row, col) { register struct ent **p; if (row < 0) row = 0; else if (row > MAXROWS - 1) row = MAXROWS - 1; if (col < 0) col = 0; else if (col > MAXCOLS - 1) col = MAXCOLS - 1; p = &tbl[row][col]; if (*p == 0) { *p = (struct ent *) malloc((unsigned) sizeof(struct ent)); if (row > maxrow) maxrow = row; if (col > maxcol) maxcol = col; (*p)->label = 0; (*p)->flags = 0; (*p)->row = row; (*p)->col = col; (*p)->expr = 0; (*p)->v = (double) 0.0; } return *p; } /* * 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. */ free_ent(p) register struct ent *p; { p->next = to_fix; to_fix = p; p->flags |= is_deleted; } flush_saved() { register struct ent *p; register struct ent *q; if (!(p = to_fix)) return; while (p) { clearent(p); q = p->next; free((char *) p); p = q; } to_fix = 0; } update() { register row, col; register struct ent **p; static lastmx = -1, lastmy, lastmw, olderrline; static char *under_cursor = " "; int maxcol; int maxrow; int rows; int cols; register r; if (winwidth <= 0) return; while (hidden_row[currow]) /* You can't hide the last row or col */ currow++; while (hidden_col[curcol]) curcol++; if (curcol < stcol) stcol = curcol, FullUpdate++; if (currow < strow) strow = currow, FullUpdate++; while (1) { register i; for (i = stcol, cols = 0, col = RESCOL; (col + fwidth[i]) < winwidth - 1 && i < MAXCOLS; i++) { cols++; if (hidden_col[i]) continue; col += fwidth[i]; } if (curcol >= stcol + cols) stcol++, FullUpdate++; else break; } while (1) { register i; for (i = strow, rows = 0, row = RESROW; row < (labline / lineheight) && i < MAXROWS; i++) { rows++; if (hidden_row[i]) continue; row++; } if (currow >= strow + rows) strow++, FullUpdate++; else break; } maxcol = stcol + cols - 1; maxrow = strow + rows - 1; if (FullUpdate) ps_StartRedraw(); ps_StartUpdate(); if (FullUpdate) { register int i; ps_eraseall(); ps_bndcolor(); ps_moveto(winwidth, labline + 1); ps_lineto(RESCOL - 1, labline + 1); ps_lineto(RESCOL - 1, 0); ps_stroke(); ps_fgcolor(); lastmx = -1; for (row = RESROW, i = strow; i <= maxrow; i++) { if (hidden_row[i]) continue; ps_drlineno(rowcoord(row) + baseoffset, i); row++; } for (col = RESCOL, i = stcol; i <= maxcol; i++) { if (hidden_col[i]) continue; col += fwidth[i]; ps_rshow(col, labline + baseoffset, coltoa(i)); } } for (row = strow, r = RESROW; row <= maxrow; row++) { register c = RESCOL; if (hidden_row[row]) continue; for (p = &tbl[row][col = stcol]; col <= maxcol; col++, p++) { if (hidden_col[col]) continue; if (*p && ((*p)->flags & is_changed || FullUpdate)) { char *s; if (!FullUpdate) ps_eraserect(c, rowcoord(r), fwidth[col], lineheight); (*p)->flags &= ~is_changed; if ((*p)->flags & is_valid) { char buf[40]; sprintf(buf, "%.*f", precision[col], (*p)->v); ps_rshow(c + fwidth[col], rowcoord(r) + baseoffset, buf); } if (s = (*p)->label) { if ((*p)->flags & is_leftflush) ps_lshow(c, rowcoord(r) + baseoffset, s); else ps_rshow(c + fwidth[col], rowcoord(r) + baseoffset, s); } } c += fwidth[col]; } r++; } if (lastmx >= 0) { ps_bgcolor(); ps_box(lastmx, lastmy, lastmw, lineheight); ps_fillbox(0, topline, winwidth, lineheight); } lastmy = RESROW; for (row = strow; row < currow; row++) if (!hidden_row[row]) lastmy += 1; lastmy = rowcoord(lastmy); lastmx = RESCOL; for (col = stcol; col < curcol; col++) if (!hidden_col[col]) lastmx += fwidth[col]; lastmw = fwidth[curcol]; ps_bndcolor(); ps_box(lastmx, lastmy, lastmw, lineheight); ps_fgcolor(); if (linelim >= 0) { ps_lshow(5, topline + baseoffset, line); } if (newerrline || FullUpdate) { if (!FullUpdate) ps_eraserect(0, msgline, winwidth, lineheight); ps_errcolor(); ps_lshow(0, msgline + baseoffset, errline); errline[0] = 0; olderrline = 1; newerrline = 0; } else if (olderrline) { olderrline = 0; if (!FullUpdate) ps_eraserect(0, msgline, winwidth, lineheight); } #ifdef undef else { if (showme) { register struct ent *p; p = tbl[currow][curcol]; if (p && ((p->flags & is_valid) || p->label)) { if (p->expr || !p->label) { linelim = 0; editexp(currow, curcol); } else { sprintf(line, "%s", p->label); } addstr("["); addstr(line); addstr("]"); linelim = -1; } else { addstr("[]"); } } } #endif if (FullUpdate) ps_EndRedraw(); FullUpdate = 0; } main(argc, argv) char **argv; { int inloop = 1; int c; int edistate = -1; int arg = 1; int narg; int nedistate; int running; int x0, y0, x1, y1; char revmsg[80]; char *revi; curfile[0] = 0; running = 1; signals(); if (ps_open_PostScript() == 0) { fprintf(stderr, "Can't contact NeWS server\n"); exit(0); } ps_initialize(); FullUpdate++; if (argc > 1) { strcpy(curfile, argv[1]); readfile(argv[1], 0); } modflg = 0; strcpy(revmsg, argv[0]); for (revi = rev; *revi++ != ':';); strcat(revmsg, revi); revi = revmsg + strlen(revmsg); *--revi = 0; strcat(revmsg, "Type '?' for help."); error(revmsg); while (inloop) { int longcom = 0; running = 1; while (running) { if (edistate < 0 && linelim < 0 && (changed || FullUpdate)) EvalAll(), changed = 0; update(); seenerr = 0; if (ps_redraw(&winwidth, &winheight)) { lineheight = 15; charwidth = 8; baseoffset = 4; topline = winheight - lineheight; msgline = topline - lineheight; labline = msgline - lineheight; FullUpdate++; { register i; for (i = 0; i < MAXCOLS; i++) if (fwidth[i] <= 0) { fwidth[i] = DEFWIDTH; precision[i] = DEFPREC; } } } else if (ps_char(&c)) { nedistate = -1; narg = 1; if (longcom) { switch ((longcom << 8) | c) { #define LC(a,b) (('a'<<8) | 'b') case ('/' << 8) + 'c': sprintf(line, "copy [to] %s%d [from] ", coltoa(curcol), currow); linelim = strlen(line); break; case ('/' << 8) + 'x': sprintf(line, "erase [v:v] "); linelim = strlen(line); break; case ('/' << 8) + 'f': sprintf(line, "fill [v:v start inc] "); linelim = strlen(line); break; case ('z' << 8) + 'r': hiderow(arg); break; case ('z' << 8) + 'c': hidecol(arg); break; case ('s' << 8) + 'r': rowshow_op(); break; case ('s' << 8) + 'c': colshow_op(); break; case ('a' << 8) + 'r': while (arg--) duprow(); break; case ('a' << 8) + 'c': while (arg--) dupcol(); break; default: error("Invalid command"); } longcom = 0; } else if (c < ' ' || c == 0177) switch (c) { case ctl('r'): case ctl('l'): FullUpdate++; break; default: error("No such command (^%c)", c + 0100); break; case ctl('b'): while (--arg >= 0) { if (curcol) curcol--; else error("At column A"); while (hidden_col[curcol] && curcol) curcol--; } break; case ctl('c'): running = 0; break; case ctl('f'): while (--arg >= 0) { if (curcol < MAXCOLS - 1) curcol++; else error("The table can't be any wider"); while (hidden_col[curcol] && (curcol < MAXCOLS - 1)) curcol++; } break; case ctl('g'): case ctl('['): linelim = -1; /*- move(1, 0); clrtoeol(); */ break; case 0177: case ctl('h'): while (--arg >= 0) if (linelim > 0) line[--linelim] = 0; break; case ctl('m'): case ctl('j'): if (linelim < 0) line[linelim = 0] = 0; else { linelim = 0; yyparse(); linelim = -1; } break; case ctl('n'): while (--arg >= 0) { if (currow < MAXROWS - 1) currow++; else error("The table can't be any longer"); while (hidden_row[currow] && (currow < MAXROWS - 1)) currow++; } break; case ctl('p'): while (--arg >= 0) { if (currow) currow--; else error("At row zero"); while (hidden_row[currow] && currow) currow--; } break; case ctl('q'): break; /* ignore flow control */ case ctl('s'): break; /* ignore flow control */ case ctl('t'): showme ^= 1; break; case ctl('u'): narg = arg * 4; nedistate = 1; break; case ctl('v'): /* insert variable name */ if (linelim > 0) { sprintf(line + linelim, "%s%d", coltoa(curcol), currow); linelim = strlen(line); } break; case ctl('e'): /* insert variable expression */ if (linelim > 0) editexp(currow, curcol); break; case ctl('a'): /* insert variable value */ if (linelim > 0) { struct ent *p = tbl[currow][curcol]; if (p && p->flags & is_valid) { sprintf(line + linelim, "%.*f", precision[curcol], p->v); linelim = strlen(line); } } break; } else if ('0' <= c && c <= '9' && (linelim < 0 || edistate >= 0)) { if (edistate != 0) { if (c == '0') /* just a '0' goes to left col */ curcol = 0; else { nedistate = 0; narg = c - '0'; } } else { nedistate = 0; narg = arg * 10 + (c - '0'); } } else if (linelim >= 0) { line[linelim++] = c; line[linelim] = 0; } else switch (c) { case '.': nedistate = 1; break; case ':': break; /* Be nice to vi users */ case '=': sprintf(line, "let %s%d = ", coltoa(curcol), currow); linelim = strlen(line); break; case '/': longcom = c; break; case '$': curcol = MAXCOLS - 1; while (!tbl[currow][curcol] && curcol > 0) curcol--; break; case '#': currow = MAXROWS - 1; while (!tbl[currow][curcol] && currow > 0) currow--; break; case '^': currow = 0; break; case '?': help(); break; case '"': sprintf(line, "label %s%d = '", coltoa(curcol), currow); linelim = strlen(line); break; case '<': sprintf(line, "leftstring %s%d = '", coltoa(curcol), currow); linelim = strlen(line); break; case '>': sprintf(line, "rightstring %s%d = '", coltoa(curcol), currow); linelim = strlen(line); break; case 'e': editv(currow, curcol); break; case 'E': edits(currow, curcol); break; case 'f': sprintf(line, "format [for column] %s [is] ", coltoa(curcol)); error("Current format is %d %d", fwidth[curcol], precision[curcol]); linelim = strlen(line); break; case 'g': sprintf(line, "goto [v] "); linelim = strlen(line); break; case 'P': sprintf(line, "put [database into] '"); if (*curfile) error("default file is '%s'", curfile); linelim = strlen(line); break; case 'M': sprintf(line, "merge [database from] '"); linelim = strlen(line); break; case 'G': sprintf(line, "get [database from] '"); if (*curfile) error("default file is '%s'", curfile); linelim = strlen(line); break; case 'W': sprintf(line, "write [listing to] '"); linelim = strlen(line); break; case 'T': /* tbl output */ sprintf(line, "tbl [listing to] '"); linelim = strlen(line); break; case 'i': switch (get_qual()) { case 'r': insertrow(arg); break; case 'c': insertcol(arg); break; default: error("Invalid insert command"); break; } break; case 'd': switch (get_qual()) { case 'r': deleterow(arg); break; case 'c': deletecol(arg); break; default: error("Invalid delete command"); break; } break; case 'v': switch (get_qual()) { case 'r': rowvalueize(arg); modflg++; break; case 'c': colvalueize(arg); modflg++; break; default: error("Invalid value command"); break; } break; case 'p': { register qual; qual = get_qual(); while (arg--) pullcells(qual); break; } case 'x': { register struct ent **p; register int c; flush_saved(); for (c = curcol; arg-- && c < MAXCOLS; c++) { p = &tbl[currow][c]; if (*p) { free_ent(*p); *p = 0; } } sync_refs(); FullUpdate++; } break; case 'Q': case 'q': running = 0; break; case 'h': while (--arg >= 0) { if (curcol) curcol--; else error("At column A"); while (hidden_col[curcol] && curcol) curcol--; } break; case 'j': while (--arg >= 0) { if (currow < MAXROWS - 1) currow++; else error("The table can't be any longer"); while (hidden_row[currow] && (currow < MAXROWS - 1)) currow++; } break; case 'k': while (--arg >= 0) { if (currow) currow--; else error("At row zero"); while (hidden_row[currow] && currow) currow--; } break; case 'l': while (--arg >= 0) { if (curcol < MAXCOLS - 1) curcol++; else error("The table can't be any wider"); while (hidden_col[curcol] && (curcol < MAXCOLS - 1)) curcol++; } break; case 'm': savedrow = currow; savedcol = curcol; break; case 'c':{ register struct ent *p = tbl[savedrow][savedcol]; register c; register struct ent *n; if (!p) break; FullUpdate++; modflg++; for (c = curcol; arg-- && c < MAXCOLS; c++) { n = lookat(currow, c); clearent(n); n->flags = p->flags; n->v = p->v; n->expr = copye(p->expr, currow - savedrow, c - savedcol); n->label = 0; if (p->label) { n->label = malloc((unsigned) (strlen(p->label) + 1)); strcpy(n->label, p->label); } } break; } case 's': case 'z': case 'a': longcom = c; break; default: if ((c & 0177) != c) error("Weird character, decimal '%d'.\n", (int) c); else error("No such command (%c)", c); break; } edistate = nedistate; arg = narg; } else if (ps_mouse(&c, &x0, &y0, &x1, &y1)) { register i, pos; int rx0 = stcol, ry0 = strow, rx1 = stcol, ry1 = strow; for (i = stcol, pos = RESCOL; pos <= winwidth && i < MAXCOLS; i++) if (!hidden_col[i]) { if (x0 >= pos) rx0 = i; if (x1 >= pos) rx1 = i; pos += fwidth[i]; } for (i = strow, pos = labline; pos > 0 && i < MAXROWS; i++) if (!hidden_row[i]) { pos -= lineheight; if (y0 < pos) ry0 = i + 1; if (y1 < pos) ry1 = i + 1; } if (rx0 > rx1) i = rx0, rx0 = rx1, rx1 = i; if (ry0 > ry1) i = ry0, ry0 = ry1, ry1 = i; switch (c) { case 1: /* Middle mouse button generates variable * reference */ if (linelim > 0) { sprintf(line + linelim, "%s%d", coltoa(rx0), ry0); linelim = strlen(line); if (rx0 != rx1 || ry0 != ry1) { sprintf(line + linelim, ":%s%d", coltoa(rx1), ry1); linelim = strlen(line); } } default: curcol = rx0; currow = ry0; break; } } else if (psio_error(PostScriptInput) || psio_eof(PostScriptInput)) { /* should probably save the file */ exit(0); } } inloop = modcheck(" before exiting"); } /* while (inloop) */ } signals() { int quit(); /*- signal(SIGINT, SIG_IGN); signal(SIGQUIT, quit); signal(SIGPIPE, quit); signal(SIGTERM, quit); signal(SIGFPE, quit); signal(SIGBUS, quit); */ } quit() { exit(1); } modcheck(endstr) char *endstr; { #ifdef undef if (modflg && curfile[0]) { char ch, lin[100]; move(0, 0); clrtoeol(); sprintf(lin, "File '%s' is modified, save%s? ", curfile, endstr); addstr(lin); refresh(); ch = nmgetch(); if (ch != 'n' && ch != 'N') if (writefile(curfile) < 0) return (1); else if (ch == ctl('g') || ch == ctl('[')) return (1); } else if (modflg) { char ch, lin[100]; move(0, 0); clrtoeol(); sprintf(lin, "Do you want a chance to save the data? "); addstr(lin); refresh(); ch = nmgetch(); if (ch == 'n' || ch == 'N') return (0); else return (1); } #endif return (0); } writefile(fname) char *fname; { register FILE *f; register struct ent **p; register r, c; char save[1024]; if (*fname == 0) fname = &curfile[0]; strcpy(save, fname); f = fopen(fname, "w"); if (f == 0) { error("Can't create %s", fname); return (-1); } fprintf(f, "# This data file was generated by the Spreadsheet "); fprintf(f, "Calculator.\n"); fprintf(f, "# You almost certainly shouldn't edit it.\n\n"); for (c = 0; c < MAXCOLS; c++) if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC) fprintf(f, "format %s %d %d\n", coltoa(c), fwidth[c], precision[c]); for (r = 0; r <= maxrow; r++) { p = &tbl[r][0]; for (c = 0; c <= maxcol; c++, p++) if (*p) { if ((*p)->label) fprintf(f, "%sstring %s%d = \"%s\"\n", (*p)->flags & is_leftflush ? "left" : "right", coltoa(c), r, (*p)->label); if ((*p)->flags & is_valid) { editv(r, c); fprintf(f, "%s\n", line); } } } fclose(f); strcpy(curfile, save); modflg = 0; error("File '%s' written.", curfile); return (0); } readfile(fname, eraseflg) char *fname; int eraseflg; { register FILE *f; char save[1024]; if (*fname == 0) fname = &curfile[0]; strcpy(save, fname); if (eraseflg && strcmp(fname, curfile) && modcheck(" first")) return; f = fopen(save, "r"); if (f == 0) { char lfname[300]; sprintf(lfname, "%s/lib/sc/%s", getenv("NEWSHOME"), fname); f = fopen(lfname, "r"); } if (f == 0) { error("Can't read %s", save); return; } if (eraseflg) erasedb(); while (fgets(line, sizeof line, f)) { linelim = 0; if (line[0] != '#') yyparse(); } fclose(f); linelim = -1; modflg++; if (eraseflg) { strcpy(curfile, save); modflg = 0; } EvalAll(); } erasedb() { register r, c; for (c = 0; c <= maxcol; c++) { fwidth[c] = DEFWIDTH; precision[c] = DEFPREC; } for (r = 0; r <= maxrow; r++) { register struct ent **p = &tbl[r][0]; for (c = 0; c++ <= maxcol; p++) if (*p) { if ((*p)->expr) efree((*p)->expr); if ((*p)->label) free((*p)->label); free((char *) *p); *p = 0; } } maxrow = 0; maxcol = 0; FullUpdate++; } #if DEBUG debugout(g, fmt, args) FILE *g; char *fmt; { int op; if (g == 0) g = fopen("debug", "a"), op = 1; if (g == 0) return; _doprnt(fmt, &args, g); fflush(g); if (op) fclose(g); } #endif