/* 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 $ * * More mods by Dan Coppersmith, 5/94 */ #include #include #include #ifdef HAVE_X11_X_H #include #include #endif /* HAVE_X11_X_H */ #ifdef SYSV #include #else #if defined(BSD42) || defined(BSD43) || defined(VMS) #include #else #include #endif #endif #include "sc.h" #ifdef HAVE_X11_X_H #include "scXstuff.h" #endif /* HAVE_X11_X_H */ #include #include static void openrow PROTO((int)); static void print_options PROTO((FILE *)); static void syncref PROTO((struct enode *)); static void unspecial PROTO((FILE *, char *, int)); static char *file_mode = "w"; /* write/append */ extern int errno; /* To store location of previous cursor postion */ extern int prvmx, prvmy, prvcol; /* line added by Bob Parbs 12-92 */ extern int lastmx, lastmy, lastcol; /* line added by Bob Parbs 12-92 */ #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); (void)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); } /* delete 'arg' rows starting at currow (deletes from currow downward) */ void deleterow(arg) register int arg; { if (any_locked_cells(currow, 0, currow + arg - 1, maxcol)) scerror("Locked cells encountered. Nothing changed"); else { flush_saved(); erase_area(currow, 0, currow + arg - 1, maxcol); currow += arg; while (--arg>=0) closerow (--currow); sync_refs(); } } #ifdef NEW void erase_area(sr, sc, er, ec) int sr, sc, er, ec; { erase_and_pull(sr, sc, er, ec, 1, 1); } void erase_and_pull(sr, sc, er, ec, eraseflg, pull) int sr, sc, er, ec; int eraseflg; int pull; { 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)) { FIXME free_ent(*pp); *pp = (struct ent *)0; } */ if (*pp) { struct ent *p = *pp; if (eraseflg && !((*pp)->flags&is_locked)) { *pp = (struct ent *)0; if (!pull) del_ent(p); /* untested */ } if (pull) { /* if am JUST pulling, need to make a copy */ if (!eraseflg) { *pp = new_ent(); copyent(*pp, p, 0, 0); (*pp)->row = p->row; (*pp)->col = p->col; } free_ent(p); } } } } } #endif 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) { sprintf(stringbuf, " Cell %s%d is locked", coltoa(c), r); scerror(stringbuf); 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) { scerror ("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); (void) clearent(n); copyent( n, p, deltar, deltac); n -> flags = p -> flags & ~is_deleted; } } void colshow_op() { register int i,j; for (i=0; i=maxcols) scerror ("No hidden columns to show"); else { (void) sprintf(line,"show %s:", coltoa(i)); (void) sprintf(line + strlen(line),"%s",coltoa(j)); linelim = strlen (line); } } void rowshow_op() { register int i,j; for (i=0; i=maxrows) scerror ("No hidden rows to show"); else { (void) sprintf(line,"show %d:%d", i, j); linelim = strlen (line); } } #ifdef notdef /* * 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. */ int get_rcqual (ch) int ch; { sprintf(stringbuf, "%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" : ""); scerror(stringbuf); if (!using_X) (void) refresh(); 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); } /*NOTREACHED*/ } #endif static 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 int 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]) != NULL) pp[0]->col += numcol; pp = ATBL(tbl, r, cs); for (c = cs; c - cs < numcol; c++, pp++) *pp = (struct ent *)0; } FullUpdate++; modflg++; } /* delete group of columns (1 or more) */ void closecol (cs, numcol) int cs; int numcol; { register r; register struct ent **pp; register struct ent *q; register c; register lim = maxcol-cs; int i; char buf[50]; if (lim - numcol < -1) { (void) sprintf(buf, "Can't delete %d column%s %d columns left", numcol, (numcol > 1 ? "s," : ","), lim+1); scerror(buf); return; } if (any_locked_cells(0, curcol, maxrow, curcol + numcol - 1)) { scerror("Locked cells encountered. Nothing changed"); return; } flush_saved(); erase_area(0, curcol, maxrow, curcol + numcol - 1); sync_refs(); /* clear then copy the block left */ lim = maxcols - numcol - 1; for (r=0; r<=maxrow; r++) { for (c = cs; c - cs < numcol; c++) if ((q = *ATBL(tbl, r, c)) != NULL) free_ent(q); pp = ATBL(tbl, r, cs); for (c=cs; c <= lim; c++, pp++) { if (c > lim) *pp = (struct ent *)0; else if ((pp[0] = pp[numcol]) != NULL) 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; 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; } scerror (""); /* 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 > maintextcols - RESCOL - 2) { sprintf(stringbuf,"Format too large - Maximum = %d", maintextcols - RESCOL - 2); scerror(stringbuf); w = maintextcols - RESCOL - 2; } if (p > w) { scerror("Precision too large"); p = w; } checkbounds(&crows, &ccols); if (ccols < c2) { sprintf(stringbuf,"Format statement failed to create implied column %d", c2); scerror(stringbuf); return; } for(i = c1; i<=c2; i++) fwidth[i] = w, precision[i] = p, realfmt[i] = r; FullUpdate++; modflg++; } static void print_options(f) FILE *f; { if( autocalc && propagation == 10 && calc_order == BYROWS && !numeric && prescale == 1.0 && !extfunc && showcell && showtop && tbl_style == 0 && craction == 0 && rowlimit == -1 && collimit == -1 ) return; /* No reason to do this */ (void) fprintf(f, "set"); if(!autocalc) (void) fprintf(f," !autocalc"); if(propagation != 10) (void) fprintf(f, " iterations = %d", propagation); if(calc_order != BYROWS ) (void) fprintf(f, " bycols"); if (numeric) (void) fprintf(f, " numeric"); if (prescale != 1.0) (void) fprintf(f, " prescale"); if (extfunc) (void) fprintf(f, " extfun"); if (!showcell) (void) fprintf(f, " !cellcur"); if (!showtop) (void) fprintf(f, " !toprow"); if (tbl_style) (void) 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) (void) fprintf(f, " craction = %d", craction); if (rowlimit >= 0) (void) fprintf(f, " rowlimit = %d", rowlimit); if (collimit >= 0) (void) fprintf(f, " collimit = %d", collimit); (void) fprintf(f, "\n"); } /* output the spreadsheet in a printer-ready form, vs sc commands */ 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 = 0; int nextcol = 0; register row, col; register struct ent **pp; /* * don't add a .asc if the user gave us a specific name * what_file() scmalloc's space */ if (*fname != '\0') fname = what_file(fname, NULL); else fname = what_file(curfile, ".asc"); if ((strcmp(fname, curfile) == 0) && !yn_ask("Confirm that you want to destroy the output file: (y,n)")) { scxfree(fname); return; } if (!pline && (pline = scxmalloc((unsigned)(FBUFLEN * ++fbufs_allocated))) == (char *)NULL) { scerror("Malloc failed in printfile()"); scxfree(fname); return; } if ((f = openout(fname, &pid)) == (FILE *)0) { sprintf(stringbuf, "Can't create file \"%s\"", fname); scerror(stringbuf); scxfree(fname); return; } scxfree(fname); 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 = scxrealloc ((char *)pline, (unsigned)(FBUFLEN * ++fbufs_allocated))) == NULL) { scerror ("Realloc failed in printfile()"); return; } } while (plinelimflags&is_valid) { while(plinelim + fwidth[col] > (fbufs_allocated * FBUFLEN)) { if((pline = ((char *)scxrealloc ((char *)pline, (unsigned)(FBUFLEN * ++fbufs_allocated)))) == NULL) { scerror ("Realloc failed in printfile()"); return; } } if ((*pp)->cellerror) (void) 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)); (void) sprintf (pline+plinelim, "%*s", fwidth[col], field); } else { char field[FBUFLEN]; (void) engformat(realfmt[col], fwidth[col], precision[col], (*pp) -> v, field, sizeof(field)); (void) sprintf (pline+plinelim, "%*s", fwidth[col], field); } } plinelim += strlen (pline+plinelim); } if ((s = (*pp)->label) != NULL) { 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 backslash is defining a repetition * char */ slen = strlen(s); if ((*s == '\\') && (*(s+1) != '\0')) slen = fwidth[col]; /* a label in the last writeable column gets all the * space it needs */ if (col == cn) fieldlen = slen; 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 *)scxrealloc ((char *)pline, (unsigned)(FBUFLEN * ++fbufs_allocated)))) == NULL) { scerror ("scxrealloc 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'; (void) fputs (pline, f); } closeout(f, pid); } char * printfile_suffix() { char *suffix = ""; switch (tbl_style) { case 0: suffix = ".cln"; break; case TBL: suffix = ".tbl"; break; case LATEX: suffix = ".lat"; break; case TEX: suffix = ".tex"; break; case FRAME: suffix = ".fram"; break; } return suffix; } void tblprintfile (fname, r0, c0, rn, cn) char *fname; int r0, c0, rn, cn; { FILE *f; int pid; register int row, col; register struct ent **pp; char coldelim = DEFCOLDELIM; /* strip off the .sc ending and add a suffix */ fname = what_file(fname, printfile_suffix()); /* scmalloc's space */ if ((strcmp(fname, curfile) == 0) && !yn_ask("Confirm that you want to destroy the table output: (y,n)")) { scxfree(fname); return; } if ((f = openout(fname, &pid)) == (FILE *)0) { sprintf(stringbuf, "Can't create file \"%s\"", fname); scerror(stringbuf); scxfree(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 Catalog\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 Catalog\n"); fprintf(f," flags&is_valid) { if ((*pp)->cellerror) { (void) 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) != NULL) { unspecial (f, s, coldelim); } } if (tbl_style == FRAME) { fprintf(f, "'>>\n"); fprintf(f," >>> # end of Cell\n"); } if ( col < cn ) if (tbl_style != FRAME) (void) fprintf(f,"%c", coldelim); } if ( tbl_style == LATEX ) { if ( row < rn ) (void) fprintf (f, "\\\\"); } else if ( tbl_style == SLATEX ) { if ( row < rn ) (void) fprintf (f, "!!"); } else if ( tbl_style == TEX ) { (void) fprintf (f, "\\cr"); } else if ( tbl_style == FRAME ) { fprintf(f," > # end of Row\n"); } (void) fprintf (f,"\n"); } if ( tbl_style == TBL ) (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname); else if ( tbl_style == LATEX ) (void) fprintf (f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname); else if ( tbl_style == SLATEX ) (void) fprintf (f,"!end\n%% ** end of %s spreadsheet output\n", progname); else if ( tbl_style == TEX ) (void) 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"); } scxfree(fname); closeout(f, pid); } /* unspecial (backquote) things that are special chars in a table */ static 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 *) scxmalloc ((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 *) scxmalloc ((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 = scxmalloc((unsigned) strlen(e->e.s)+1); (void) 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) */ p = *ATBL(tbl, i, j); if (p && p->expr) syncref(p->expr); } } static 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; } } } /* mark a row as hidden */ void hiderow(arg) int arg; { register int r1; register int r2; r1 = currow; r2 = r1 + arg - 1; if (r1 < 0 || r1 > r2) { scerror ("Invalid range"); return; } if (r2 >= maxrows-1) { if (!growtbl(GROWROW, arg+1, 0)) { scerror("You can't hide the last row"); return; } } FullUpdate++; modflg++; while (r1 <= r2) row_hidden[r1++] = 1; } /* mark a column as hidden */ void hidecol(arg) int arg; { register int c1; register int c2; c1 = curcol; c2 = c1 + arg - 1; if (c1 < 0 || c1 > c2) { scerror ("Invalid range"); return; } if (c2 >= maxcols-1) { if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1)) { scerror("You can't hide the last col"); return; } } FullUpdate++; modflg++; while (c1 <= c2) col_hidden[c1++] = TRUE; } /* mark a row as not-hidden */ void showrow(r1, r2) int r1, r2; { if (r1 < 0 || r1 > r2) { scerror ("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) { scerror ("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 = NULL; 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, file_mode)); } #if defined(MSDOS) scerror("Piping not available under MS-DOS\n"); return(0); #else fname++; /* Skip | */ if ( pipe (pipefd) < 0) { scerror("Can't make pipe to child"); *rpid = 0; return(0); } if (!using_X) deraw(); #ifdef VMS fprintf(stderr, "No son tasks available yet under VMS--sorry\n"); #else /* VMS */ if ((pid=fork()) == 0) /* if child */ { (void) close (0); /* close stdin */ (void) close (pipefd[1]); (void) dup (pipefd[0]); /* connect to pipe input */ (void) signal (SIGINT, SIG_DFL); /* reset */ (void) execl ("/bin/sh", "sh", "-c", fname, 0); exit (-127); } else /* else parent */ { *rpid = pid; if ((f = fdopen (pipefd[1], "w")) == (FILE *)0) { (void) kill (pid, -9); scerror ("Can't fdopen output"); (void) 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; (void) fclose (f); #if !defined(MSDOS) if (pid) { while (pid != wait(&temp)) /**/; (void) printf("Press RETURN to continue "); (void) fflush(stdout); (void) nmgetch(); if (!using_X) goraw(); } #endif /* MSDOS */ } void copyent(n,p,dr,dc) register struct ent *n, *p; int dr, dc; { if(!n||!p) { scerror("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 = scxmalloc ((unsigned) (strlen (p -> label) + 1)); (void) strcpy (n -> label, p -> label); } n -> format = 0; if (p -> format) { n -> format = scxmalloc ((unsigned) (strlen (p -> format) + 1)); (void) strcpy (n -> format, p -> format); } } /* output ascii sc commands */ void write_fd (f, r0, c0, rn, cn) register FILE *f; int r0, c0, rn, cn; { register struct ent **pp; register r, c; (void) fprintf (f, "# This data file was generated by the Spreadsheet "); (void) fprintf (f, "Calculator.\n"); (void) fprintf (f, "# You almost certainly shouldn't edit it.\n\n"); graphic_write_defn(f); /* write graph definitions, if any exist */ print_options(f); for (c=0; clabel || (*pp)->flags&is_strexpr) { edits(r,c); (void) fprintf(f, "%s\n",line); } if ((*pp)->flags&is_valid) { editv (r, c); (void) fprintf (f, "%s\n",line); } if ((*pp)->format) { editfmt (r, c); (void) fprintf (f, "%s\n",line); } if ((*pp)->flags&is_locked) (void) 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; int pid, cret; if (*fname == '\0') fname = curfile; fname = what_file(fname, NULL); #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) if (Crypt) { cret = cwritefile(fname, r0, c0, rn, cn); scxfree(fname); return(cret); } #endif /* VMS */ if ((f= openout(fname, &pid)) == (FILE *)0) { sprintf(stringbuf, "Can't create file \"%s\"", fname); scerror(stringbuf); scxfree(fname); return (-1); } write_fd(f, r0, c0, rn, cn); closeout(f, pid); if (!pid) { (void) strcpy(curfile, fname); modflg = 0; sprintf(stringbuf,"File \"%s\" written.",curfile); scerror(stringbuf); } scxfree(fname); return (0); } void readfile (fname,eraseflg) char *fname; int eraseflg; { register FILE *f; int tempautolabel; tempautolabel = autolabel; /* turn off auto label */ autolabel = 0; /* when reading a file */ if (*fname == '\0') fname = curfile; fname = what_file(fname, NULL); #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) if (Crypt) { creadfile(fname, eraseflg); scxfree(fname); return; } #endif /* VMS */ if (eraseflg && strcmp(fname,curfile) && modcheck(" first")) { scxfree(fname); return; } if ((f = fopen(findhome(fname), "r")) == (FILE *)0) { sprintf(stringbuf, "Can't read file \"%s\"", fname); scerror(stringbuf); scxfree(fname); return; } if (eraseflg) erasedb(); loading++; while (fgets(line, sizeof(line), f)) { linelim = 0; if (line[0] == 'G') /* indicates graph definitions */ graphic_read_defn(f); else if (line[0] != '#') (void) yyparse (); } --loading; (void) fclose (f); linelim = -1; modflg++; if (eraseflg) { (void) strcpy(curfile,fname); modflg = 0; } autolabel = tempautolabel; EvalAll(); scxfree(fname); } /* read in a file as strings into the startrow at startcol, * and clipping at endcol. */ void readstrfile(fname, startrow, startcol, endrow, endcol) char *fname; int startrow, startcol; int endrow, endcol; { register FILE *f; int tempautolabel; struct ent *e; int sr; char *cp; tempautolabel = autolabel; /* turn off auto label when */ autolabel = 0; /* when reading a file */ if (*fname == '\0') fname = curfile; fname = what_file(fname, NULL); if ((f = fopen(findhome(fname), "r")) == (FILE *)0) { sprintf(stringbuf, "Can't read file \"%s\"", fname); scerror(stringbuf); scxfree(fname); return; } loading++; for (sr = startrow; fgets(line, sizeof(line), f); sr++) { if (endrow != -1 && sr > endrow) break; linelim = 0; /* clip the newline if there is one. */ cp = &line[strlen(line)-1]; if (*cp == '\n' || *cp == '\r') *cp = 0; e = lookat(sr, startcol); (void)label(e, line, -1); } sprintf(stringbuf, "Read %d lines.", sr-startrow); scerror(stringbuf); --loading; /* go back to where we started */ moveto(startrow, startcol); (void) fclose (f); linelim = -1; modflg++; 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) scxfree ((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; { prvmx=lastmx; prvmy=lastmy, prvcol=lastcol; /* line added by Bob Parbs 12-92 */ while (--arg>=0) { if (curcol) curcol--; else { scerror ("At column A"); break; } while(col_hidden[curcol] && curcol) curcol--; } } /* moves curcol forward one displayed column */ void forwcol(arg) int arg; { prvmx=lastmx; prvmy=lastmy; prvcol=lastcol; /* line added by Bob Parbs 12-92 */ 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 { scerror ("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, do_stand) 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 */ int do_stand; /* if standout needed */ { 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; #ifdef HAVE_X11_X_H XEvent event; char buffer[8]; /* I'd rather use line 1 than line 0, because that is where the user will * be looking most often. (B.Backman) */ if (using_X) { clearlines(1,1); XDrawImageString(dpy, mainwin, maingc, textcol(0), textrow(1), msg, strlen(msg)); while (1) { XPeekEvent(dpy, &event); while (event.type == MotionNotify) XNextEvent(dpy,&event); if (event.type == KeyPress) { XNextEvent(dpy,&event); if(XLookupString(&(event.xkey),buffer,8,0,0)) { ch = buffer[0]; if (ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N') { if (ch == ctl('g') || ch == ESC) return(-1); scerror("y or n response required"); return (-1); } if (ch == 'y' || ch == 'Y') return(1); else return(0); } } else { scerror("Y or N keypress is required"); return(-1); } } return(-1); /* never reached */ } else #endif /* HAVE_X11_X_H */ { (void) move (0, 0); (void) clrtoeol (); (void) addstr (msg); (void) refresh(); ch = nmgetch(); if ( ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N' ) { if (ch == ctl('g') || ch == ESC) return(-1); scerror("y or n response required"); return (-1); } if (ch == 'y' || ch == 'Y') return(1); else return(0); } /* HAVE_X11_X_H, end curses */ } /* 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; 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; #ifndef __STDC__ extern struct passwd *getpwnam(); #endif char *namep; char name[50]; namep = name; while ((*pathptr != '\0') && (*pathptr != '/')) *(namep++) = *(pathptr++); *namep = '\0'; if ((pwent = getpwnam(name)) == NULL) { (void) 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 #ifdef NOTUSED /****************************************** * Determine default dir to be printed out, * based on mdir. Check for // **********/ void get_default_dir(char *tmp) { int length; if (mdir) strcpy(tmp, mdir); #ifdef JEFFB /* see my notes */ else { /* make home dir the default dir */ strcpy(tmp, getenv("HOME")); if (tmp == NULL) *tmp = '/'; } #endif /* append exactly one / to end of tmp */ length = strlen(tmp); if ((tmp[length-1] == '/')&&(tmp[length-2] == '/')) tmp[length-1] = '\0'; else if (tmp[length-1] != '/') { tmp[length] = '/'; tmp[length+1] = '\0'; } } /*************************************************************** * give tmp the string to be printed out as the default path, as * determined by mdir and curfile, which may also be adjusted ***/ void get_default_path(char *tmp) { int i, len_mdir, len_curfile, len_same, length; char tmp2[PATHLEN]; #ifdef JEFFB /* see my notes */ if (!(mdir)&&!(*curfile)) /* give tmp the home dir, with 1 / at end */ { strcpy(tmp, getenv("HOME")); if (tmp == NULL) *tmp = '/'; else /* this is to add a / to end of default dir */ { length = strlen(tmp); tmp[length] = '/'; tmp[length +1] = '\0'; } } else #endif if((mdir)&&!(*curfile)) /* copy mdir to tmp, with 1 / at end */ { length = strlen(mdir); strcpy(tmp, mdir); if ((tmp[length -1] == '/')&&(tmp[length -2] == '/')) tmp[length -1] = '\0'; if (tmp[length -1] != '/') { tmp[length] = '/'; tmp[length +1] = '\0'; } } else if(!(mdir)&&(*curfile)) /* print out most recent curfile */ strcpy(tmp, curfile); else /* ((mdir)&&(*curfile)), find best solution */ { len_mdir = strlen(mdir); len_curfile = strlen(curfile); if (strcmp(mdir, "/") == 0) { if (*curfile == '/') strcpy(tmp, curfile); else /* tmp = /curfile */ { tmp[0] = '/'; strcpy(tmp2, curfile); for (i = 1; i <= len_curfile; i++) tmp[i] = tmp2[i -1]; } return; } /* find out how many initial chars are the same */ strcpy(tmp, curfile); strcpy(tmp2, mdir); for (i = 0; (i < len_mdir)&&(i < len_curfile); i++) if (tmp[i] != tmp2[i]) break; len_same = i; if (len_same < 3) /* leave it alone */ return; else { /* find length of last word in curfile */ for (i = len_curfile -1; i > 0; i--) if (tmp[i] == '/') /* strange, no / present */ return; /* now copy entire path minus filename to mdir */ strcpy(tmp2, tmp); /* note: length of mdir can be shortened, */ tmp2[i +1] = '\0'; /* but not increased. This is */ strcpy(mdir, tmp2);/* still better than nothing. */ } } } /* get_default_path */ #endif /* NOTUSED */ /* * 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]; #if HAVE_ST_BLKSIZE 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); (void) sprintf(tpp, "#%s~", fname); if (stat(path, &statbuf) == 0) { /* if we know the optimum block size, use it */ #ifdef HAVE_ST_BLKSIZE if ((statbuf.st_blksize > buflen) || (buf == NULL)) { buflen = statbuf.st_blksize; if ((buf = scxrealloc(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 HAVE_ST_BLKSIZE 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 /* DOBACKUP */ #ifdef JEFFB /* old version */ /* replace a possible '.sc', '.s', or '.' suffix with "ending" */ char * fsuffix(fname, ending) char *fname; char *ending; { char *newname, *chp, *slp; if ((newname = scxmalloc(strlen(fname)+1+strlen(ending))) != NULL) { strcpy(newname, fname); /* * chp will point to the start of the filename, path seperator */ if ((slp = strrchr(newname, '/')) != NULL) { *slp = '\0'; chp = slp+1; } else chp = newname; /* start of .sc? */ if ((chp = strrchr(chp, '.')) != NULL) { if (strncmp(chp, ".sc", strlen(chp)) == 0) *chp = '\0'; } /* put the path '/' back */ if (slp != NULL) *slp = '/'; strcat(newname, ending); } return(newname); } #endif /* JEFFB */ /* * if ending !NULL: replace a possible '.sc', '.s', or '.' suffix with "ending" * if path !NULL: add to front (remove starting '*' from fname) */ char * fsuffix(path, fname, ending) char *path, *fname; char *ending; { char *newname, *chp, *slp; int len; len = strlen(fname)+1; if (path != NULL) len += strlen(path); if (ending != NULL) len += strlen(ending); if ((newname = scxmalloc(len)) != NULL) { if (path != NULL) /* remove leading '*', using mdir */ sprintf(newname, "%s/%s", path, fname+1); else strcpy(newname, fname); if (ending == NULL) /* no suffix to replace */ return(newname); /* * chp will point to the start of the filename, path seperator */ if ((slp = strrchr(newname, '/')) != NULL) { *slp = '\0'; chp = slp+1; } else chp = newname; /* start of .sc? */ if ((chp = strrchr(chp, '.')) != NULL) { if (strncmp(chp, ".sc", strlen(chp)) == 0) *chp = '\0'; } /* put the path '/' back */ if (slp != NULL) *slp = '/'; strcat(newname, ending); } return(newname); } /* used by routines which will accept a >> designation on a filename * used to specify append to a file */ static char * extract_filename(fname) char *fname; { /* default mode */ file_mode = "w"; while (isspace(*fname)) fname++; /* "> file" will be interpreted as write, ">> file" as append */ if ((*fname == '>') && (*++fname == '>')) { file_mode = "a"; fname++; } while (isspace(*fname)) fname++; return fname; } char * what_file(fname, suffix) char *fname, *suffix; { fname = extract_filename(fname); if (*fname == '*' && mdir) fname = fsuffix(mdir, fname, suffix); else fname = fsuffix(NULL, fname, suffix); return(fname); }