/* SC A Spreadsheet Calculator * Command routines * * original by James Gosling, September 1982 * modifications by Mark Weiser and Bruce Israel, * University of Maryland * * More mods Robert Bond, 12/86 * * $Revision: 1.1.1.1 $ */ /* patched in some of the sc Rev 6.21 stuff. Art Mulder, 5/92 */ #ifndef lint static char Sccsid[] = "%W% %G%"; #endif /* * Include Files */ #include #if defined(BSD42) || defined(BSD43) # include #else # ifndef SYSIII # include # endif #endif #include "curses_stuff.h" #if defined(BSD42) || defined(BSD43) || defined(VMS) # include #else # include #endif #include #include #include "ss.h" #include "keys.h" /* * Function Prototypes */ #ifdef SYSV3 extern void exit(); #else extern int exit(); #endif void openrow(); void syncref(); void unspecial(); extern int errno; /* a linked list of free [struct ent]'s, uses .next as the pointer */ extern struct ent *freeents; /* a linked list of free [struct enodes]'s, uses .e.o.left as the pointer */ extern struct enode *freeenodes; #define DEFCOLDELIM ':' /* copy the current row (currow) and place the cursor in the new row */ void duprow() { if (currow >= maxrows - 1 || maxrow >= maxrows - 1) { if (!growtbl(GROWROW, 0, 0)) return; } modflg++; currow++; openrow (currow); for (curcol = 0; curcol <= maxcol; curcol++) { register struct ent *p = *ATBL(tbl, currow - 1, curcol); if (p) { register struct ent *n; n = lookat (currow, curcol); copyent ( n, p, 1, 0); } } for (curcol = 0; curcol <= maxcol; curcol++) { register struct ent *p = *ATBL(tbl, currow, curcol); if (p && (p -> flags & is_valid) && !p -> expr) break; } if (curcol > maxcol) curcol = 0; } /* copy the current column (curcol) and place the cursor in the new column */ void dupcol() { if (curcol >= maxcols - 1 || maxcol >= maxcols - 1) { if (!growtbl(GROWCOL, 0, 0)) return; } modflg++; curcol++; opencol (curcol, 1); for (currow = 0; currow <= maxrow; currow++) { register struct ent *p = *ATBL(tbl, currow, curcol - 1); if (p) { register struct ent *n; n = lookat (currow, curcol); copyent ( n, p, 0, 1); } } for (currow = 0; currow <= maxrow; currow++) { register struct ent *p = *ATBL(tbl, currow, curcol); if (p && (p -> flags & is_valid) && !p -> expr) break; } if (currow > maxrow) currow = 0; } /* insert 'arg' rows before currow */ void insertrow(arg) register int arg; { while (--arg>=0) openrow (currow); } void deleterow(startrow,endrow) /*---------------------------------------------------------------------- ** Delete the rows from 'startrow' to 'endrow'. (startrow must be ** <= endrow.) */ register int startrow,endrow; { if (any_locked_cells(startrow, 0, endrow, maxcol)) error("Locked cells encountered. Nothing changed"); else { flush_saved(); erase_area(startrow, 0, endrow, maxcol); /* currow += arg; ** while (--arg>=0) closerow (--currow); */ currow = startrow; while (startrow <= endrow) closerow(endrow--); sync_refs(); } } void erase_area(sr, sc, er, ec) int sr, sc, er, ec; { register int r, c; register struct ent **pp; if (sr > er) { r = sr; sr = er; er= r; } if (sc > ec) { c = sc; sc = ec; ec= c; } if (sr < 0) sr = 0; if (sc < 0) sc = 0; checkbounds(&er, &ec); for (r = sr; r <= er; r++) { for (c = sc; c <= ec; c++) { pp = ATBL(tbl, r, c); if (*pp && !((*pp)->flags&is_locked)) { free_ent(*pp); *pp = (struct ent *)0; } } } } /* * deletes the expression associated w/ a cell and turns it into a constant * containing whatever was on the screen */ void valueize_area(sr, sc, er, ec) int sr, sc, er, ec; { register int r, c; register struct ent *p; if (sr > er) { r = sr; sr = er; er= r; } if (sc > ec) { c = sc; sc = ec; ec= c; } if (sr < 0) sr = 0; if (sc < 0) sc = 0; checkbounds(&er, &ec); for (r = sr; r <= er; r++) { for (c = sc; c <= ec; c++) { p = *ATBL(tbl, r, c); if (p && p->flags&is_locked) { error(" Cell %s%d is locked", coltoa(c), r); continue; } if (p && p->expr) { efree(p->expr); p->expr = (struct enode *)0; p->flags &= ~is_strexpr; } } } } void pullcells(to_insert) int to_insert; { register struct ent *p, *n; register int deltar, deltac; int minrow, mincol; int mxrow, mxcol; int numrows, numcols; if (! to_fix) { error ("No data to pull"); return; } minrow = maxrows; mincol = maxcols; mxrow = 0; mxcol = 0; for (p = to_fix; p; p = p->next) { if (p->row < minrow) minrow = p->row; if (p->row > mxrow) mxrow = p->row; if (p->col < mincol) mincol = p->col; if (p->col > mxcol) mxcol = p->col; } numrows = mxrow - minrow + 1; numcols = mxcol - mincol + 1; deltar = currow - minrow; deltac = curcol - mincol; if (to_insert == 'r') { insertrow(numrows); deltac = 0; } else if (to_insert == 'c') { opencol(curcol, numcols); deltar = 0; } FullUpdate++; modflg++; for (p = to_fix; p; p = p->next) { n = lookat (p->row + deltar, p->col + deltac); clearent(n); copyent( n, p, deltar, deltac); n -> flags = p -> flags & ~is_deleted; } } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** Art Mulder Modified: See "rowcolumn.c" ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** void ** colshow_op() ** { ** register int i,j; ** for (i=0; i=maxcols) ** error ("No hidden columns to show"); ** else { ** Sprintf(line,"show %s:", coltoa(i)); ** Sprintf(line + strlen(line),"%s",coltoa(j)); ** linelim = strlen (line); ** } ** } ** ** void ** rowshow_op() ** { ** register int i,j; ** for (i=0; i=maxrows) ** error ("No hidden rows to show"); ** else { ** Sprintf(line,"show %d:%d", i, j); ** linelim = strlen (line); ** } ** } */ /* * Given a row/column command letter, emit a small menu, then read a qualifier * character for a row/column command and convert it to 'r' (row), 'c' * (column), or 0 (unknown). If ch is 'p', an extra qualifier 'm' is allowed. */ /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** Art Mulder Modified: No Longer Used. Jan 25/93 ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** int ** get_rcqual (ch) ** int ch; ** { ** error ("%sow/column: r: row c: column%s", ** (ch == 'i') ? "Insert r" : ** (ch == 'a') ? "Append r" : ** (ch == 'd') ? "Delete r" : ** (ch == 'p') ? "Pull r" : ** (ch == 'v') ? "Values r" : ** (ch == 'z') ? "Zap r" : ** (ch == 's') ? "Show r" : "R", ** ** (ch == 'p') ? " m: merge" : ""); ** ** switch (nmgetch()) ** { ** case 'r': ** case 'l': ** case 'h': ** case ctl('f'): ** case ctl('b'): return ('r'); ** ** case 'c': ** case 'j': ** case 'k': ** case ctl('p'): ** case ctl('n'): return ('c'); ** ** case 'm': return ((ch == 'p') ? 'm' : 0); ** ** case ESC: ** case ctl('g'): return (ESC); ** ** default: return (0); ** } ** ** } */ void openrow (rs) int rs; { register r, c; struct ent **tmprow, **pp; if (rs > maxrow) maxrow = rs; if (maxrow >= maxrows - 1 || rs > maxrows - 1) { if (!growtbl(GROWROW, rs, 0)) return; } /* * save the last active row+1, shift the rows downward, put the last * row in place of the first */ tmprow = tbl[++maxrow]; for (r = maxrow; r > rs; r--) { row_hidden[r] = row_hidden[r-1]; tbl[r] = tbl[r-1]; pp = ATBL(tbl, r, 0); for (c = 0; c < maxcols; c++, pp++) if (*pp) (*pp)->row = r; } tbl[r] = tmprow; /* the last row was never used.... */ FullUpdate++; modflg++; } /* delete row r */ void closerow (r) register r; { register struct ent **pp; register c; struct ent **tmprow; if (r > maxrow) return; /* save the row and empty it out */ tmprow = tbl[r]; pp = ATBL(tbl, r, 0); for (c=maxcol+1; --c>=0; pp++) { if (*pp) { free_ent(*pp); *pp = (struct ent *)0; } } /* move the rows, put the deleted, but now empty, row at the end */ for (; r < maxrows - 1; r++) { row_hidden[r] = row_hidden[r+1]; tbl[r] = tbl[r+1]; pp = ATBL(tbl, r, 0); for (c = 0; c < maxcols; c++, pp++) if (*pp) (*pp)->row = r; } tbl[r] = tmprow; maxrow--; FullUpdate++; modflg++; } void opencol (cs, numcol) int cs; int numcol; { register r; register struct ent **pp; register c; register lim = maxcol-cs+1; int i; if (cs > maxcol) maxcol = cs; maxcol += numcol; if ((maxcol >= maxcols - 1) && !growtbl(GROWCOL, 0, maxcol)) return; for (i = maxcol; i > cs; i--) { fwidth[i] = fwidth[i-numcol]; precision[i] = precision[i-numcol]; realfmt[i] = realfmt[i-numcol]; col_hidden[i] = col_hidden[i-numcol]; } for (c = cs; c - cs < numcol; c++) { fwidth[c] = DEFWIDTH; precision[c] = DEFPREC; realfmt[c] = DEFREFMT; } for (r=0; r<=maxrow; r++) { pp = ATBL(tbl, r, maxcol); for (c=lim; --c>=0; pp--) if (pp[0] = pp[-numcol]) pp[0]->col += numcol; pp = ATBL(tbl, r, cs); for (c = cs; c - cs < numcol; c++, pp++) *pp = (struct ent *)0; } FullUpdate++; modflg++; } /** void closecol (cs, numcol) **/ void closecol (startcol, endcol) /*---------------------------------------------------------------------- ** Delete the cols from 'startcol' to 'endcol'. (startcol must be ** <= endcol.) */ int startcol,endcol; { int cs = startcol; /** Old var's **/ int numcol = (endcol - startcol + 1); /** Old var's **/ register r; register struct ent **pp; register struct ent *q; register c; register lim = maxcol-cs; int i; char buf[50]; /* Check for errors */ if (endcol > maxcol) { error("Ending Column exceeds Max column, No Change."); return; } if (any_locked_cells(0, startcol, maxrow, endcol)) { error("Locked cells encountered. Nothing changed"); return; } /* Clear the columns */ flush_saved(); erase_area(0, startcol, maxrow, endcol); sync_refs(); /* clear then copy the block left */ lim = maxcols - numcol - 1; /* ?? */ for (r=0; r<=maxrow; r++) { for (c = startcol; c <= endcol; c++) if (q = *ATBL(tbl, r, c)) free_ent(q); /** NOT sure how this works **/ pp = ATBL(tbl, r, startcol); for (c=startcol; c <= lim; c++, pp++) { if (c > lim) *pp = (struct ent *)0; else if (pp[0] = pp[numcol]) pp[0]->col -= numcol; } c = numcol; for (; --c >= 0; pp++) *pp = (struct ent *)0; } for (i = cs; i < maxcols - numcol - 1; i++) { fwidth[i] = fwidth[i+numcol]; precision[i] = precision[i+numcol]; realfmt[i] = realfmt[i+numcol]; col_hidden[i] = col_hidden[i+numcol]; } for (; i < maxcols - 1; i++) { fwidth[i] = DEFWIDTH; precision[i] = DEFPREC; realfmt[i] = DEFREFMT; col_hidden[i] = FALSE; } maxcol -= numcol; curcol = startcol; FullUpdate++; modflg++; } void doend(rowinc, colinc) int rowinc, colinc; { register struct ent *p; int r, c; if (VALID_CELL(p, currow, curcol)) { r = currow + rowinc; c = curcol + colinc; if (r >= 0 && r < maxrows && c >= 0 && c < maxcols && !VALID_CELL(p, r, c)) { currow = r; curcol = c; } } if (!VALID_CELL(p, currow, curcol)) { switch (rowinc) { case -1: while (!VALID_CELL(p, currow, curcol) && currow > 0) currow--; break; case 1: while (!VALID_CELL(p, currow, curcol) && currow < maxrows-1) currow++; break; case 0: switch (colinc) { case -1: while (!VALID_CELL(p, currow, curcol) && curcol > 0) curcol--; break; case 1: while (!VALID_CELL(p, currow, curcol) && curcol < maxcols-1) curcol++; break; } break; } error (""); /* clear line */ return; } switch (rowinc) { case -1: while (VALID_CELL(p, currow, curcol) && currow > 0) currow--; break; case 1: while (VALID_CELL(p, currow, curcol) && currow < maxrows-1) currow++; break; case 0: switch (colinc) { case -1: while (VALID_CELL(p, currow, curcol) && curcol > 0) curcol--; break; case 1: while (VALID_CELL(p, currow, curcol) && curcol < maxcols-1) curcol++; break; } break; } if (!VALID_CELL(p, currow, curcol)) { currow -= rowinc; curcol -= colinc; } } /* Modified 9/17/90 THA to handle more formats */ void doformat(c1,c2,w,p,r) int c1,c2,w,p,r; { register int i; int crows = 0; int ccols = c2; if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ; if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ; if (w > COLS - RESCOL - 2) { error("Format too large - Maximum = %d", COLS - RESCOL - 2); w = COLS-RESCOL-2; } if (p > w) { error("Precision too large"); p = w; } checkbounds(&crows, &ccols); if (ccols < c2) { error("Format statement failed to create implied column %d", c2); return; } for(i = c1; i<=c2; i++) fwidth[i] = w, precision[i] = p, realfmt[i] = r; FullUpdate++; modflg++; } void print_options(f) FILE *f; { if( autocalc && propagation == 10 && calc_order == BYROWS && prescale == 1.0 && !extfunc && showcell && showtop && tbl_style == 0 && craction == 0 && rowlimit == -1 && collimit == -1 ) return; /* No reason to do this */ Fprintf(f, "set"); if(!autocalc) Fprintf(f," !autocalc"); if(propagation != 10) Fprintf(f, " iterations = %d", propagation); if(calc_order != BYROWS ) Fprintf(f, " bycols"); if (prescale != 1.0) Fprintf(f, " prescale"); if (extfunc) Fprintf(f, " extfun"); if (!showcell) Fprintf(f, " !cellcur"); if (!showtop) Fprintf(f, " !toprow"); if (tbl_style) Fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" : tbl_style == LATEX ? "latex" : tbl_style == SLATEX ? "slatex" : tbl_style == TEX ? "tex" : tbl_style == FRAME ? "frame" : "0" ); if (craction) Fprintf(f, " craction = %d", craction); if (rowlimit >= 0) Fprintf(f, " rowlimit = %d", rowlimit); if (collimit >= 0) Fprintf(f, " collimit = %d", collimit); Fprintf(f, "\n"); } void printfile (fname, r0, c0, rn, cn) char *fname; int r0, c0, rn, cn; { FILE *f; static char *pline = NULL; /* only malloc once, malloc is slow */ static unsigned fbufs_allocated = 0; int plinelim; int pid; int fieldlen, nextcol; register row, col; register struct ent **pp; if ((strcmp(fname, curfile) == 0) && !yn_ask("Confirm that you want to destroy the data base: (y,n)")) { return; } if (!pline && (pline = Malloc((unsigned)(FBUFLEN * ++fbufs_allocated))) == (char *)NULL) { error("Malloc failed in printfile()"); return; } if ((f = openout(fname, &pid)) == (FILE *)0) { error ("Can't create file \"%s\"", fname); return; } for (row=r0;row<=rn; row++) { register c = 0; if (row_hidden[row]) continue; pline[plinelim=0] = '\0'; for (pp = ATBL(tbl, row, col=c0); col<=cn; pp += nextcol-col, col = nextcol, c += fieldlen) { nextcol = col+1; if (col_hidden[col]) { fieldlen = 0; continue; } fieldlen = fwidth[col]; if (*pp) { char *s; /* * dynamically allocate pline, making sure we are not * attempting to write 'out of bounds'. */ while(c > (fbufs_allocated * FBUFLEN)) { if((pline = Realloc((char *)pline, (unsigned)(FBUFLEN * ++fbufs_allocated)) ) == NULL) { error ("Realloc failed in printfile()"); return; } } while (plinelimflags&is_valid) { while(plinelim + fwidth[col] > (fbufs_allocated * FBUFLEN)) { if((pline = ((char *)Realloc((char *)pline, (unsigned)(FBUFLEN * ++fbufs_allocated)))) == NULL) { error ("Realloc failed in printfile()"); return; } } if ((*pp)->cellerror) Sprintf (pline+plinelim, "%*s", fwidth[col], ((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID")); else { if ((*pp)->format) { char field[FBUFLEN]; format ((*pp)->format, (*pp)->v, field, sizeof(field)); Sprintf (pline+plinelim, "%*s", fwidth[col], field); } else { char field[FBUFLEN]; (void) engformat(realfmt[col], fwidth[col], precision[col], (*pp) -> v, field, sizeof(field)); Sprintf (pline+plinelim, "%*s", fwidth[col], field); } } plinelim += strlen (pline+plinelim); } if (s = (*pp)->label) { int slen; char *start, *last; register char *fp; struct ent *nc; /* * Figure out if the label slops over to a blank field. A * string started with backslah is defining repition char */ slen = strlen(s); if ( *s == '\\' && *(s+1)!= '\0' ) slen = fwidth[col]; while (slen > fieldlen && nextcol <= cn && !((nc = lookat(row,nextcol))->flags & is_valid) && !(nc->label)) { if (!col_hidden[nextcol]) fieldlen += fwidth[nextcol]; nextcol++; } if (slen > fieldlen) slen = fieldlen; while(c + fieldlen + 2 > (fbufs_allocated * FBUFLEN)) { if((pline = ((char *)Realloc((char *)pline, (unsigned)(FBUFLEN * ++fbufs_allocated)))) == NULL) { error ("Realloc failed in printfile()"); return; } } /* Now justify and print */ start = (*pp)->flags & is_leftflush ? pline + c : pline + c + fieldlen - slen; if( (*pp)->flags & is_label ) start = pline + (c + ((fwidth[col]>slen)?(fwidth[col]-slen)/2:0)); last = pline + c + fieldlen; fp = plinelim < c ? pline + plinelim : pline + c; while (fp < start) *fp++ = ' '; if( *s == '\\' && *(s+1)!= '\0' ) { char *strt; strt = ++s; while(slen--) { *fp++ = *s++; if( *s == '\0' ) s = strt; } } else while (slen--) *fp++ = *s++; if (!((*pp)->flags & is_valid) || fieldlen != fwidth[col]) while(fp < last) *fp++ = ' '; if (plinelim < fp - pline) plinelim = fp - pline; } } } pline[plinelim++] = '\n'; pline[plinelim] = '\0'; Fputs (pline, f); } closeout(f, pid); } void tblprintfile (fname, r0, c0, rn, cn) char *fname; int r0, c0, rn, cn; { FILE *f; int pid; register row, col; register struct ent **pp; char coldelim = DEFCOLDELIM; if ((strcmp(fname, curfile) == 0) && !yn_ask("Confirm that you want to destroy the data base: (y,n)")) return; if ((f = openout(fname, &pid)) == (FILE *)0) { error ("Can't create file \"%s\"", fname); return; } if ( tbl_style == TBL ) { fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname); fprintf(f,"tab(%c);\n",coldelim); for (col=c0;col<=cn; col++) fprintf(f," n"); fprintf(f, ".\n"); } else if ( tbl_style == LATEX ) { fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname); for (col=c0;col<=cn; col++) fprintf(f,"c"); fprintf(f, "}\n"); coldelim = '&'; } else if ( tbl_style == SLATEX ) { fprintf(f,"%% ** %s spreadsheet output\n!begin<",progname); for (col=c0;col<=cn; col++) fprintf(f,"c"); fprintf(f, ">\n"); coldelim = '&'; } else if ( tbl_style == TEX ) { fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n", progname, cn-c0+1); coldelim = '&'; } else if ( tbl_style == FRAME ) { fprintf(f, " # generated by the sc spreadsheet calculator\n "); fprintf(f," # This table's ID is 1\n"); fprintf(f," # Table Format Catalog\n"); fprintf(f," > # end of TblFormat\n"); fprintf(f," # Has %d columns\n",cn-c0+1,cn-c0+1); fprintf(f," # Forces lookup in Paragraph Format Catalog\n"); fprintf(f," \n",fname); fprintf(f," > # end of ParaLine\n"); fprintf(f," > # end of Para\n"); fprintf(f," > # end of TblTitleContent\n"); fprintf(f," # in Paragraph Format Catal og\n"); fprintf(f," >\n",'A'+col); fprintf(f," >>> # end of Cell\n"); } fprintf(f," > # end of Row\n"); fprintf(f," > # end of TblH\n"); fprintf(f," # in Paragraph Format Cata log\n"); fprintf(f," flags&is_valid) { if ((*pp)->cellerror) { Fprintf (f, "%*s", fwidth[col], ((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID")); } else if ((*pp)->format) { char field[FBUFLEN]; (void) format ((*pp)->format, (*pp)->v, field, sizeof(field)); unspecial (f, field, coldelim); } else { char field[FBUFLEN]; (void) engformat(realfmt[col], fwidth[col], precision[col], (*pp) -> v, field, sizeof(field)); unspecial (f, field, coldelim); } } if (s = (*pp)->label) { unspecial (f, s, coldelim); } } if (tbl_style == FRAME) { fprintf(f, "'>>\n"); fprintf(f," >>> # end of Cell\n"); } if ( col < cn ) if (tbl_style != FRAME) Fprintf(f,"%c", coldelim); } if ( tbl_style == LATEX ) { if ( row < rn ) Fprintf (f, "\\\\"); } else if ( tbl_style == SLATEX ) { if ( row < rn ) Fprintf (f, "!!"); } else if ( tbl_style == TEX ) { Fprintf (f, "\\cr"); } else if ( tbl_style == FRAME ) { fprintf(f," > # end of Row\n"); } Fprintf (f,"\n"); } if ( tbl_style == TBL ) Fprintf (f, ".TE\n.\\\" ** end of %s spreadsheet output\n", progname); else if ( tbl_style == LATEX ) Fprintf (f, "\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname); else if ( tbl_style == SLATEX ) Fprintf (f, "!end\n%% ** end of %s spreadsheet output\n", progname); else if ( tbl_style == TEX ) Fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname); else if ( tbl_style == FRAME ) { fprintf(f," > # end of TblBody\n"); fprintf(f," ># end of Tbl\n"); fprintf(f,"> # end of Tbls\n"); fprintf(f," \n"); fprintf(f," > # Reference to table ID 1\n"); fprintf(f,">>\n"); } closeout(f, pid); } /* unspecial (backquote) things that are special chars in a table */ void unspecial(f, str, delim) FILE *f; char *str; int delim; { if( *str == '\\' ) str++; /* delete wheeling string operator, OK? */ while (*str) { if (((tbl_style == LATEX) || (tbl_style == SLATEX) || (tbl_style == TEX)) && ((*str == delim) || (*str == '$') || (*str == '#') || (*str == '%') || (*str == '{') || (*str == '}') || (*str == '[') || (*str == ']') || (*str == '&'))) putc('\\', f); putc(*str, f); str++; } } struct enode * copye (e, Rdelta, Cdelta) register struct enode *e; int Rdelta, Cdelta; { register struct enode *ret; if (e == (struct enode *)0) { ret = (struct enode *)0; } else if (e->op & REDUCE) { int newrow, newcol; if (freeenodes) { ret = freeenodes; freeenodes = ret->e.o.left; } else ret = (struct enode *) Malloc((unsigned) sizeof (struct enode)); ret->op = e->op; newrow=e->e.r.left.vf & FIX_ROW ? e->e.r.left.vp->row : e->e.r.left.vp->row+Rdelta; newcol=e->e.r.left.vf & FIX_COL ? e->e.r.left.vp->col : e->e.r.left.vp->col+Cdelta; ret->e.r.left.vp = lookat (newrow, newcol); ret->e.r.left.vf = e->e.r.left.vf; newrow=e->e.r.right.vf & FIX_ROW ? e->e.r.right.vp->row : e->e.r.right.vp->row+Rdelta; newcol=e->e.r.right.vf & FIX_COL ? e->e.r.right.vp->col : e->e.r.right.vp->col+Cdelta; ret->e.r.right.vp = lookat (newrow, newcol); ret->e.r.right.vf = e->e.r.right.vf; } else { if (freeenodes) { ret = freeenodes; freeenodes = ret->e.o.left; } else ret = (struct enode *) Malloc((unsigned) sizeof (struct enode)); ret->op = e->op; switch (ret->op) { case 'v': { int newrow, newcol; newrow=e->e.v.vf & FIX_ROW ? e->e.v.vp->row : e->e.v.vp->row+Rdelta; newcol=e->e.v.vf & FIX_COL ? e->e.v.vp->col : e->e.v.vp->col+Cdelta; ret->e.v.vp = lookat (newrow, newcol); ret->e.v.vf = e->e.v.vf; break; } case 'k': ret->e.k = e->e.k; break; case 'f': ret->e.o.right = copye (e->e.o.right,0,0); ret->e.o.left = (struct enode *)0; break; case '$': ret->e.s = Malloc((unsigned) strlen(e->e.s)+1); Strcpy(ret->e.s, e->e.s); break; default: ret->e.o.right = copye (e->e.o.right,Rdelta,Cdelta); ret->e.o.left = copye (e->e.o.left,Rdelta,Cdelta); break; } } return ret; } /* * sync_refs and syncref are used to remove references to * deleted struct ents. Note that the deleted structure must still * be hanging around before the call, but not referenced by an entry * in tbl. Thus the free_ent calls in sc.c */ void sync_refs () { register i,j; register struct ent *p; sync_ranges(); for (i=0; i<=maxrow; i++) for (j=0; j<=maxcol; j++) if ((p = *ATBL(tbl, i, j)) && p->expr) syncref(p->expr); } void syncref(e) register struct enode *e; { if (e == (struct enode *)0) return; else if (e->op & REDUCE) { e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col); e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col); } else { switch (e->op) { case 'v': e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col); break; case 'k': break; case '$': break; default: syncref(e->e.o.right); syncref(e->e.o.left); break; } } } void hiderow(startrow,endrow) /*---------------------------------------------------------------------- ** Mark the rows from 'startrow' to 'endrow' (inclusive) as hidden. */ register int startrow,endrow; { currow = startrow; if (startrow < 0 || startrow > endrow) { error ("Invalid range"); return; } if (endrow >= maxrows-1) { if (!growtbl(GROWROW, (endrow - startrow +1), 0)) { error("You can't hide the last row"); return; } } FullUpdate++; modflg++; while (startrow <= endrow) row_hidden[startrow++] = 1; } void hidecol(startcol,endcol) /*---------------------------------------------------------------------- ** Mark the cols from 'startcol' to 'endcol' (inclusive) as hidden. */ register int startcol,endcol; { curcol = startcol; if (startcol < 0 || startcol > endcol) { error ("Invalid range"); return; } if (endcol >= maxcols-1) { if ((endcol >= ABSMAXCOLS) || !growtbl(GROWCOL, 0, (endcol-startcol +1))) { error("You can't hide the last col"); return; } } FullUpdate++; modflg++; while (startcol <= endcol) col_hidden[startcol++] = TRUE; } /* mark a row as not-hidden */ void showrow(r1, r2) int r1, r2; { if (r1 < 0 || r1 > r2) { error ("Invalid range"); return; } if (r2 > maxrows-1) { r2 = maxrows-1; } FullUpdate++; modflg++; while (r1 <= r2) row_hidden[r1++] = 0; } /* mark a column as not-hidden */ void showcol(c1, c2) int c1, c2; { if (c1 < 0 || c1 > c2) { error ("Invalid range"); return; } if (c2 > maxcols-1) { c2 = maxcols-1; } FullUpdate++; modflg++; while (c1 <= c2) col_hidden[c1++] = FALSE; } /* Open the output file, setting up a pipe if needed */ FILE * openout(fname, rpid) char *fname; int *rpid; { int pipefd[2]; int pid; FILE *f; char *efname; while (*fname && (*fname == ' ')) /* Skip leading blanks */ fname++; if (*fname != '|') { /* Open file if not pipe */ *rpid = 0; efname = findhome(fname); #ifdef DOBACKUPS if (!backup_file(efname) && (yn_ask("Could not create backup copy, Save anyhow?: (y,n)") != 1)) return(0); #endif return(fopen(efname, "w")); } #if defined(MSDOS) error("Piping not available under MS-DOS\n"); return(0); #else fname++; /* Skip | */ if ( pipe (pipefd) < 0) { error("Can't make pipe to child"); *rpid = 0; return(0); } deraw(); #ifdef VMS fprintf(stderr, "No child tasks available yet under VMS--sorry\n"); #else /* not VMS */ if ((pid=fork()) == 0) /* if child */ { Close (0); /* close stdin */ Close (pipefd[1]); Dup (pipefd[0]); /* connect to pipe input */ Signal (SIGINT, SIG_DFL); /* reset */ Execl ("/bin/sh", "sh", "-c", fname, 0); exit (-127); } else /* else parent */ { *rpid = pid; if ((f = fdopen (pipefd[1], "w")) == (FILE *)0) { Kill (pid, -9); error ("Can't fdopen output"); Close (pipefd[1]); *rpid = 0; return(0); } } #endif /* VMS */ return(f); #endif /* MSDOS */ } /* close a file opened by openout(), if process wait for return */ void closeout(f, pid) FILE *f; int pid; { int temp; Fclose (f); #if !defined(MSDOS) if (pid) { while (pid != wait(&temp)) /**/; Printf("Press RETURN to continue "); Fflush(stdout); (void) nmgetch(); goraw(); } #endif /* MSDOS */ } void copyent(n,p,dr,dc) register struct ent *n, *p; int dr, dc; { if(!n||!p){error("internal error");return;} n -> v = p -> v; n -> flags = p -> flags; n -> expr = copye (p -> expr, dr, dc); n -> label = (char *)0; if (p -> label) { n -> label = Malloc((unsigned) (strlen (p -> label) + 1)); Strcpy (n -> label, p -> label); } n -> format = 0; if (p -> format) { n -> format = Malloc((unsigned) (strlen (p -> format) + 1)); Strcpy (n -> format, p -> format); } } void write_fd (f, r0, c0, rn, cn) register FILE *f; int r0, c0, rn, cn; { register struct ent **pp; register r, c; extern char *v_name(); 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"); print_options(f); for (c=0; clabel) { edits(r,c); Fprintf(f, "%s\n",line); } if ((*pp)->flags&is_valid) { editv (r, c); Fprintf (f, "%s\n",line); } if ((*pp)->format) { editfmt (r, c); Fprintf (f, "%s\n",line); } if ((*pp)->flags&is_locked) Fprintf(f, "lock %s%d\n", coltoa((*pp)->col), (*pp)->row) ; } } if (rndinfinity) fprintf(f, "set rndinfinity\n"); fprintf(f, "goto %s\n", v_name( currow, curcol ) ); } int writefile (fname, r0, c0, rn, cn) char *fname; int r0, c0, rn, cn; { register FILE *f; char save[PATHLEN]; int pid; #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) if (Crypt) { return (cwritefile(fname, r0, c0, rn, cn)); } #endif /* VMS */ if (*fname == '\0') fname = curfile; Strcpy(save,fname); if ((f= openout(fname, &pid)) == (FILE *)0) { error ("Can't create file \"%s\"", fname); return (-1); } write_fd(f, r0, c0, rn, cn); closeout(f, pid); if (!pid) { Strcpy(curfile, save); modflg = 0; error("File \"%s\" written.",curfile); } return (0); } void readfile (fname,eraseflg) char *fname; int eraseflg; { register FILE *f; char save[PATHLEN]; int tempautolabel; tempautolabel = autolabel; /* turn off auto label when */ autolabel = 0; /* when reading a file */ if (*fname == '*' && mdir) { Strcpy(save, mdir); *fname = '/'; Strcat(save, fname); } else { if (*fname == '\0') fname = curfile; Strcpy(save,fname); } #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) if (Crypt) { creadfile(save, eraseflg); return; } #endif /* VMS */ if (eraseflg && strcmp(fname,curfile) && modcheck(" first")) return; if ((f = fopen(findhome(save), "r")) == (FILE *)0) { error ("Can't read file \"%s\"", save); return; } if (eraseflg) erasedb (); loading++; while (fgets(line, sizeof(line), f)) { linelim = 0; if (line[0] != '#') Yyparse (); } --loading; Fclose (f); linelim = -1; modflg++; if (eraseflg) { Strcpy(curfile,save); modflg = 0; } autolabel = tempautolabel; EvalAll(); } /* erase the database (tbl, etc.) */ void erasedb () { register r, c; for (c = 0; c<=maxcol; c++) { fwidth[c] = DEFWIDTH; precision[c] = DEFPREC; realfmt[c] = DEFREFMT; } for (r = 0; r<=maxrow; r++) { register struct ent **pp = ATBL(tbl, r, 0); for (c=0; c++<=maxcol; pp++) if (*pp) { if ((*pp)->expr) efree ((*pp) -> expr); if ((*pp)->label) Free ((char *)((*pp) -> label)); (*pp)->next = freeents; /* save [struct ent] for reuse */ freeents = *pp; *pp = (struct ent *)0; } } maxrow = 0; maxcol = 0; clean_range(); FullUpdate++; } /* moves curcol back one displayed column */ void backcol(arg) int arg; { while (--arg>=0) { if (curcol) curcol--; else {error ("At column A"); break;} while(col_hidden[curcol] && curcol) curcol--; } } /* moves curcol forward one displayed column */ void forwcol(arg) int arg; { while (--arg>=0) { if (curcol < maxcols - 1) curcol++; else if (!growtbl(GROWCOL, 0, arg)) /* get as much as needed */ break; else curcol++; while(col_hidden[curcol]&&(curcol=0) { if (currow < maxrows - 1) currow++; else if (!growtbl(GROWROW, arg, 0)) /* get as much as needed */ break; else currow++; while (row_hidden[currow]&&(currow=0) { if (currow) currow--; else {error ("At row zero"); break;} while (row_hidden[currow] && currow) currow--; } } /* * Show a cell's label string or expression value. May overwrite value if * there is one already displayed in the cell. Created from old code in * update(), copied with minimal changes. */ void showstring (string, dirflush, hasvalue, row, col, nextcolp, mxcol, fieldlenp, r, c) char *string; /* to display */ int dirflush; /* or rightflush or centered */ int hasvalue; /* is there a numeric value? */ int row, col; /* spreadsheet location */ int *nextcolp; /* value returned through it */ int mxcol; /* last column displayed? */ int *fieldlenp; /* value returned through it */ int r, c; /* screen row and column */ { register int nextcol = *nextcolp; register int fieldlen = *fieldlenp; char field[FBUFLEN]; int slen; char *start, *last; register char *fp; struct ent *nc; /* This figures out if the label is allowed to slop over into the next blank field */ slen = strlen (string); if( *string == '\\' && *(string+1)!= '\0' ) slen = fwidth[col]; while ((slen > fieldlen) && (nextcol <= mxcol) && !((nc = lookat (row, nextcol)) -> flags & is_valid) && !(nc->label)) { if (! col_hidden [nextcol]) fieldlen += fwidth [nextcol]; nextcol++; } if (slen > fieldlen) slen = fieldlen; /* Now justify and print */ start = (dirflush&is_leftflush) ? field : field + fieldlen - slen; if( dirflush & is_label ) start = field + ((slenop) { case UPPER: case LOWER: case CAPITAL: case O_SCONST: case '#': case DATE: case FMT: case STINDEX: case EXT: case SVAL: case SUBSTR: return (STR); case '?': case IF: return(etype(e->e.o.right->e.o.left)); case 'f': return(etype(e->e.o.right)); case O_VAR: { register struct ent *p; p = e->e.v.vp; if (p->expr) return(p->flags & is_strexpr ? STR : NUM); else if (p->label) return(STR); else return(NUM); } default: return(NUM); } } /* return 1 if yes given, 0 otherwise */ int yn_ask(msg) char *msg; { char ch; Move (0, 0); Clrtoeol (); Addstr (msg); ch = nmgetch(); if ( ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N' ) { if (ch == ctl('g') || ch == ESC) return(-1); error("y or n response required"); return (-1); } if (ch == 'y' || ch == 'Y') return(1); else return(0); } /* expand a ~ in a path to your home directory */ #if !defined(MSDOS) && !defined(VMS) #include #endif char * findhome(path) char *path; { static char *HomeDir = NULL; extern char *getenv(); if (*path == '~') { char *pathptr; char tmppath[PATHLEN]; if (HomeDir == NULL) { HomeDir = getenv("HOME"); if (HomeDir == NULL) HomeDir = "/"; } pathptr = path + 1; if ((*pathptr == '/') || (*pathptr == '\0')) { strcpy(tmppath, HomeDir); } #if !defined(MSDOS) && !defined(VMS) else { struct passwd *pwent; extern struct passwd *getpwnam(); char *namep; char name[50]; namep = name; while ((*pathptr != '\0') && (*pathptr != '/')) *(namep++) = *(pathptr++); *namep = '\0'; if ((pwent = getpwnam(name)) == NULL) { Sprintf(path, "Can't find user %s", name); return(NULL); } strcpy(tmppath, pwent->pw_dir); } #endif strcat(tmppath, pathptr); strcpy(path, tmppath); } return(path); } #ifdef DOBACKUPS #include /* * make a backup copy of a file, use the same mode and name in the format * [path/]#file~ * return 1 if we were successful, 0 otherwise */ int backup_file(path) char *path; { struct stat statbuf; char fname[PATHLEN]; char tpath[PATHLEN]; #ifdef sequent static char *buf = NULL; static unsigned buflen = 0; #else char buf[BUFSIZ]; #endif char *tpp; int infd, outfd; int count; /* tpath will be the [path/]file ---> [path/]#file~ */ strcpy(tpath, path); if ((tpp = strrchr(tpath, '/')) == NULL) tpp = tpath; else tpp++; strcpy(fname, tpp); Sprintf(tpp, "#%s~", fname); if (stat(path, &statbuf) == 0) { /* if we know the optimum block size, use it */ #ifdef sequent if ((statbuf.st_blksize > buflen) || (buf == NULL)) { buflen = statbuf.st_blksize; if ((buf = Realloc(buf, buflen)) == (char *)0) { buflen = 0; return(0); } } #endif if ((infd = open(path, O_RDONLY, 0)) < 0) return(0); if ((outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT, statbuf.st_mode)) < 0) return(0); #ifdef sequent while((count = read(infd, buf, statbuf.st_blksize)) > 0) #else while((count = read(infd, buf, sizeof(buf))) > 0) #endif { if (write(outfd, buf, count) != count) { count = -1; break; } } close(infd); close(outfd); return((count < 0) ? 0 : 1); } else if (errno == ENOENT) return(1); return(0); } #endif static int day_month_starts[12] = {0,31,59,90,120,151,181,212,243,273,304,334}; double convert_date(d, m, y) int d; int m; int y; { /* Convert to years since 1970. (or 2000, fix by 2070) */ if (y > 1970) y -= 1970; /* Full year given */ else if (y >= 70) y -= 70; /* Years since 1900 */ else y += 30; /* Years since 2000 */ /* Use quarter days to compensate for leap years. */ return (double)((y * (365 * 4 + 1) + day_month_starts[m-1] * 4 + d * 4 - 2) * 6 * 60 * 60); }