4.4BSD/usr/src/contrib/sc/cmds.c

Compare this file to the similar file:
Show the results in this format:

/*	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: 6.8 $
 */

#include <curses.h>
#if defined(BSD42) || defined(BSD43)
#include <sys/file.h>
#else
#include <fcntl.h>
#endif
#include "sc.h"
#include <signal.h>
#include <errno.h>

#ifdef BSD42
#include <strings.h>
#else
#ifndef SYSIII
#include <string.h>
#endif
#endif

#ifdef SYSV3
extern void exit();
#else
extern int exit();
#endif

extern	int	errno;

#define DEFCOLDELIM ':'

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;
}

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;
}

void
insertrow(arg)
register int arg;
{
    while (--arg>=0) openrow (currow);
}

void
deleterow(arg)
register int arg;
{
    flush_saved();
    erase_area(currow, 0, currow + arg - 1, maxcol);
    currow += arg;
    while (--arg>=0) closerow (--currow);
    sync_refs();
}

void
rowvalueize(arg)
register int arg;
{
    valueize_area(currow, 0, currow + arg - 1, maxcol);
}

void
colvalueize(arg)
register int arg;
{
    valueize_area(0, curcol, maxrow, curcol + arg - 1);
}

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) {
		free_ent(*pp);
		*pp = (struct ent *)0;
	    }
	}
    }
}

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->expr) {
		efree(p, 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);
	(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; i++)
	if (col_hidden[i]) 
	    break;
    for(j=i; j<maxcols; j++)
	if (!col_hidden[j])
	    break;
    j--;
    if (i>=maxcols)
	error ("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; i++)
	if (row_hidden[i]) 
	    break;
    for(j=i; j<maxrows; j++)
	if (!row_hidden[j]) {
	    break;
	}
    j--;

    if (i>=maxrows)
	error ("No hidden rows to show");
    else {
	(void) 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.
 */

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" : "");

    (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*/
}

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++;
}

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 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];
	col_hidden[i] = col_hidden[i-numcol];
    }
    for (c = cs; c - cs < numcol; c++)
    {	fwidth[c] = DEFWIDTH;
	precision[c] =  DEFPREC;
    }
	
    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)
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)
    {	sprintf(buf, "Can't delete %d column%s %d columns left", numcol,
			(numcol > 1 ? "s," : ","), lim+1);
	error(buf);
	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))
			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])
		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];
	col_hidden[i] = col_hidden[i+numcol];
    }
    for (; i < maxcols - 1; i++) {
	fwidth[i] = DEFWIDTH;
	precision[i] = DEFPREC;
	col_hidden[i] = 0;
    }

    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;
        }

	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;
    }
}

void
doformat(c1,c2,w,p)
int c1,c2,w,p;
{
    register int i;

    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;
    }

    for(i = c1; i<=c2; i++)
	fwidth[i] = w, precision[i] = p;

    FullUpdate++;
    modflg++;
}

void
print_options(f)
FILE *f;
{
    if(
       autocalc &&
       propagation == 10 &&
       calc_order == BYROWS &&
       !numeric &&
       prescale == 1.0 &&
       !extfunc &&
       showcell &&
       showtop &&
       tbl_style == 0
      )
		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 == TEX ? "tex" : "0" );
    (void) fprintf(f, "\n");
}

void
printfile (fname, r0, c0, rn, cn)
char *fname;
int r0, c0, rn, cn;
{
    FILE *f;
    char pline[FBUFLEN];
    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 ((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;

		while (plinelim<c) pline[plinelim++] = ' ';
		plinelim = c;
		if ((*pp)->flags&is_valid) {
		    (void)sprintf (pline+plinelim,"%*.*f",fwidth[col],
		                                precision[col], (*pp)->v);
		    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 */
		    slen = strlen(s);
		    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;
		    
		    /* Now justify and print */
		    start = (*pp)->flags & is_leftflush ? pline + c
					: pline + c + fieldlen - slen;
		    last = pline + c + fieldlen;
		    fp = plinelim < c ? pline + plinelim : pline + c;
		    while (fp < start)
			*fp++ = ' ';
		    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);
}

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 == TEX ) {
	fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n",
		progname, cn-c0+1);
	coldelim = '&';
	}

    for (row=r0; row<=rn; row++) {
	if ( tbl_style == TEX )
	    (void) fprintf (f, "\\+");
	
	for (pp = ATBL(tbl, row, col=c0); col<=cn; col++, pp++) {
	    if (*pp) {
		char *s;
		if ((*pp)->flags&is_valid) {
		    (void) fprintf (f,"%.*f",precision[col],
				(*pp)->v);
		}
		if (s = (*pp)->label) {
	            (void) fprintf (f,"%s",s);
		}
	    }
	    if ( col < cn )
		(void) fprintf(f,"%c",coldelim);
	}
	if ( tbl_style == LATEX ) {
	    if ( row < rn ) (void) fprintf (f, "\\\\");
	    }
	else if ( tbl_style == TEX ) {
	    (void) fprintf (f, "\\cr");
	    }
	(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 == TEX )
    (void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname);

    closeout(f, pid);
}

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;
	ret = (struct enode *) xmalloc ((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 {
	ret = (struct enode *) xmalloc ((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 = xmalloc((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, fix_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(arg)
int arg;
{
    register int r1;
    register int r2;

    r1 = currow;
    r2 = r1 + arg - 1;
    if (r1 < 0 || r1 > r2) {
	error ("Invalid range");
	return;
    }
    if (r2 >= maxrows-1)
    {	if (!growtbl(GROWROW, arg+1, 0))
	{	error("You can't hide the last row");
		return;
	}
    }
    FullUpdate++;
    modflg++;
    while (r1 <= r2)
	row_hidden[r1++] = 1;
}

void
hidecol(arg)
int arg;
{
    register int c1;
    register int c2;

    c1 = curcol;
    c2 = c1 + arg - 1;
    if (c1 < 0 || c1 > c2) {
	error ("Invalid range");
	return;
    }
    if (c2 >= maxcols-1)
    {	if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1))
	{	error("You can't hide the last col");
		return;
	}
    }
    FullUpdate++;
    modflg++;
    while (c1 <= c2)
	col_hidden[c1++] = 1;
}

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;
}

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++] = 0;
}

/* 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"));
    }

    fname++;				/* Skip | */
    if ( pipe (pipefd) < 0) {
	error("Can't make pipe to child");
	*rpid = 0;
	return(0);
    }

    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);
	    error ("Can't fdopen output");
	    (void) close (pipefd[1]);
	    *rpid = 0;
	    return(0);
	}
    }
#endif /* VMS */
    return(f);
}

void
closeout(f, pid)
FILE *f;
int pid;
{
    int temp;

    (void) fclose (f);
    if (pid) {
         while (pid != wait(&temp)) /**/;
	 (void) printf("Press RETURN to continue ");
	 (void) fflush(stdout);
	 (void) nmgetch();
	 goraw();
    }
}

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 = (char *)
		xmalloc  ((unsigned) (strlen (p -> label) + 1));
	(void) strcpy (n -> label, p -> label);
    }
}

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");
    print_options(f);
    for (c=0; c<maxcols; c++)
	if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC)
	    (void) fprintf (f, "format %s %d %d\n",coltoa(c),fwidth[c],precision[c]);
    for (c=c0; c<cn; c++) {
        if (col_hidden[c]) {
            (void) fprintf(f, "hide %s\n", coltoa(c));
        }
    }
    for (r=r0; r<=rn; r++) {
	if (row_hidden[r]) {
	    (void) fprintf(f, "hide %d\n", r);
	}
    }

    write_range(f);

    if (mdir) 
	    (void) fprintf(f, "mdir \"%s\"\n", mdir);
    for (r=r0; r<=rn; r++) {
	pp = ATBL(tbl, r, c0);
	for (c=c0; c<=cn; c++, pp++)
	    if (*pp) {
		if ((*pp)->label) {
		    edits(r,c);
		    (void) fprintf(f, "%s\n",line);
		}
		if ((*pp)->flags&is_valid) {
		    editv (r, c);
		    (void) fprintf (f, "%s\n",line);
		}
	    }
    }
}

int
writefile (fname, r0, c0, rn, cn)
char *fname;
int r0, c0, rn, cn;
{
    register FILE *f;
    char save[PATHLEN];
    int pid;

#ifndef VMS
    if (Crypt) {
	return (cwritefile(fname, r0, c0, rn, cn));
    }
#endif /* VMS */

    if (*fname == '\0') fname = curfile;

    (void) 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) {
        (void) 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];

    if (*fname == '*' && mdir) { 
       (void) strcpy(save, mdir);
       *fname = '/';
       (void) strcat(save, fname);
    } else {
        if (*fname == '\0')
	    fname = curfile;
        (void) strcpy(save,fname);
    }

#ifndef VMS
    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] != '#') (void) yyparse ();
    }
    --loading;
    (void) fclose (f);
    linelim = -1;
    modflg++;
    if (eraseflg) {
	(void) strcpy(curfile,save);
	modflg = 0;
    }
    EvalAll();
}

void
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 **pp = ATBL(tbl, r, 0);
	for (c=0; c++<=maxcol; pp++)
	    if (*pp) {
		if ((*pp)->expr) efree (*pp, (*pp) -> expr);
		if ((*pp)->label) xfree ((char *)((*pp) -> label));
		xfree ((char *)(*pp));
		*pp = (struct ent *)0;
	    }
    }
    maxrow = 0;
    maxcol = 0;
    clean_range();
    FullUpdate++;
}

void
backcol(arg)
	int arg;
{
    while (--arg>=0) {
	if (curcol)
	    curcol--;
	else
	    {error ("At column A"); break;}
	while(col_hidden[curcol] && curcol)
	    curcol--;
    }
}

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;
	while(col_hidden[curcol]&&(curcol<maxcols-1))
	    curcol++;
    }
}

void
forwrow(arg)
	int arg;
{
    while (--arg>=0) {
	if (currow < maxrows - 1)
	    currow++;
	else
	if (!growtbl(GROWROW, arg, 0))	/* get as much as needed */
		break;
	while (row_hidden[currow]&&(currow<maxrows-1))
	    currow++;
    }
}

void
backrow(arg)
	int arg;
{
    while (--arg>=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, leftflush, hasvalue, row, col, nextcolp, mxcol, fieldlenp, r, c)
    char *string;	/* to display */
    int leftflush;	/* or rightflush */
    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);
    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 = leftflush ? field : field + fieldlen - slen;
    last = field+fieldlen;
    fp = field;
    while (fp < start)
	*fp++ = ' ';
    while (slen--)
	*fp++ = *string++;
    if ((! hasvalue) || fieldlen != fwidth[col]) 
	while (fp < last)
	    *fp++ = ' ';
    *fp = '\0';
#ifdef VMS
    mvaddstr(r, c, field);	/* this is a macro */
#else
    (void) mvaddstr(r, c, field);
#endif

    *nextcolp  = nextcol;
    *fieldlenp = fieldlen;
}

int
etype(e)
register struct enode *e;
{
    if (e == (struct enode *)0)
	return NUM;
    switch (e->op) {
    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;

	(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);
		error("y or n response required");
		return (-1);
	}
	if (ch == 'y' || ch == 'Y')
		return(1);
	else
		return(0);
}

#include <pwd.h>
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);
		}
		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);
		}

		strcat(tmppath, pathptr);
		strcpy(path, tmppath);
	}
	return(path);
}

#ifdef DOBACKUPS
#include <sys/types.h>
#include <sys/stat.h>

/*
 * 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
	char	*buf;
#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)
	{
#ifdef sequent
		if ((buf = xmalloc(statbuf.st_blksize)) == (char *)0)
			return(0);
#endif

		if ((infd = open(path, O_RDONLY, 0)) < 0)
		{
#ifdef sequent
			xfree(buf);
#endif
			return(0);
		}
		if ((outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT,
					statbuf.st_mode)) < 0)
		{
#ifdef sequent
			xfree(buf);
#endif
			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);
#ifdef sequent
		xfree(buf);
#endif
		return((count < 0) ? 0 : 1);
	}
	else
	if (errno == ENOENT)
		return(1);
	return(0);
}
#endif