[comp.sources.misc] v20i035: sc - The SC Spreadsheet, release 6.16, Part01/07

buhrt@prslnk.uucp (Jeff Buhrt) (06/06/91)

Submitted-by: Jeff Buhrt <prslnk!buhrt@cs.indiana.edu>
Posting-number: Volume 20, Issue 35
Archive-name: sc/part01

This is Sc 6.16, a public domain spreadsheet for Unix.
It has been tested on most flavors of Unix, an Amiga Sc6.16 is being
worked on.

See the README for installation instructions (configure Makefile and
make install), CHANGES for what has happened since Sc6.1
(and Sc6.8 that was on comp.sources.unix). 

Some high points new since Sc6.8 are: scqref a quick reference card,
many new features, bug fixes, per cell formatting,
a way of relative addressing cells, many more things.


                -Jeff Buhrt
                iuvax!prslnk!buhrt
                sequent!sawmill!prslnk!buhrt
                812-275-0750 work
----
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  README cmds.c sc.h
# Wrapped by kent@sparky on Wed Jun  5 09:22:19 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive 1 (of 7)."'
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
  echo shar: Extracting \"'README'\" \(3689 characters\)
  sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is a much modified version of the public domain spread sheet sc,
Xposted several years ago by Mark Weiser as vc, originally by James Gosling.
X
XCHANGES lists the changes since 6.1 to 6.15.
XCurrent maintainer: {sequent, tippy.cs.purdue.edu}!sawmill!buhrt (Jeff Buhrt)
X
XWhen you get it built, try "sc tutorial.sc" for a simple introduction
Xto the basic commands.
X
XTo print a quick reference card, type the command:
X	scqref | [your_printer_commmand]
X
XIf you have the command 'file' that uses /etc/magic add the line:
X38	string		Spreadsheet	sc file
X
XPsc formats ascii files for use in the spread sheet.  If you don't have
Xgetopts, there is a public domain version by Henry Spencer hidden away in
Xthe VMS_NOTES file.
X
XI have modified the Makefile to make it easy for you to call the
Xprogram what you want.  Just change "name=sc" and "NAME=SC" to
X"name=myfavoritename" and "NAME=MYFAVORITENAME" and try "make
Xmyfavoritename".
X
XSimilarly, you can make the documentation with "make myfavoritename.man".
X"make install" will make and install the code in EXDIR.  The
Xinstallation steps and documentation all key off of the name.  The
Xmakefile even changes the name in the nroffable man page.  If you don't
Xhave nroff, you will have to change sc.man yourself.
X
XThis release has been tested against a Sequent S81 running DYNIX 3.0.17
X(BSD 4.2):cc, atscc, gcc, AT&T SysV 3.2.2:cc, gcc, ESIX SysV 3.2 Rev D:cc, gcc.
XJust check the Makefile for the system flags.   I have heard
Xreports of lots of other machines that work. If you have problems with
Xlex.c, and don't care about arrow keys, define SIMPLE (-DSIMPLE in the
Xmakefile).  SIMPLE causes the arrow keys to not be used.
X
XIf you have problems with your yacc saying: too many terminals ...127...
XComment out the gram.c and y.tab.c code in Makefile and uncomment the
Xsection that uses mygram.c and myy.tab.h.
X
XGuidelines for Hackers:
X
XIf you want to send changes you have made to SC, please feel free to do
Xso.  If they work :-) and seem worthwhile, I'll put them in.  Please
Xrefrain from wholesale "style" or "cleanup" changes.  It is easy to add
Xyour changes but it makes it hard to merge in the next guy's stuff if
Xhe used the release as a base.  Leave my $Revision:  identifiers alone-
Xthey help me track what you used as a base.  If you check the code into
Xrcs, delete the "$"s on the Revison lines before you do.
X
XYou may not like 4 space indenting and curly braces on the "if" line, but
Xyour code will look like that before it leaves my hands so you may as well
Xabide by the style in the code when you make your changes.  I have also been
Xknown to break things trying to make them look "right".  If you do string
Xfunctions, please, PLEASE pay attention to null pointers, use xmalloc,
Xxrealloc, and xfree; and xfree those arguments.  And don't forget to
Xdocument your changes in both help.c and sc.doc.
X
XDisclaimer:
X
XStarting 4/4/90: (I will be maintaining Sc for a while at least,
X	Robert Bond has been very busy lately)
XSc is not a product of Grauel Enterprises, Inc.  It is supplied as
Xis with no warranty, express or implied, as a service to Usenet readers.
XIt is not copyrighted, either.  Have at it.
X
XArchives:
X1) (FTP) jpd@usl.edu James Dugal
X	pc.usl.edu
X
X2) (UUCP) marc@dumbcat.sf.ca.us (Marco S Hyman)
X    dumbcat Any ACU 9600 14157850194 "" \d\r in:--in: nuucp word: guest
X    dumbcat Any ACU 2400 14157850194 "" \d\r in:-BREAK-in: nuucp word: guest
X    dumbcat Any ACU 1200 14157850194 "" \d\r in:-BREAK-in:-BREAK-in: nuucp word: guest
X  Note: dumbcat speaks 9600 at V.32 -- sorry, this is not a Telebit modem.
X  (Grab dumbcat!~/INDEX for a complete list)
X
X					Jeff Buhrt
X					Grauel Enterprises, Inc.
X					sequent!sawmill!buhrt
X
END_OF_FILE
  if test 3689 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
  fi
  # end of 'README'
fi
if test -f 'cmds.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmds.c'\"
else
  echo shar: Extracting \"'cmds.c'\" \(36608 characters\)
  sed "s/^X//" >'cmds.c' <<'END_OF_FILE'
X/*	SC	A Spreadsheet Calculator
X *		Command routines
X *
X *		original by James Gosling, September 1982
X *		modifications by Mark Weiser and Bruce Israel,
X *			University of Maryland
X *
X *              More mods Robert Bond, 12/86
X *
X *		$Revision: 6.16 $
X */
X
X#include <sys/types.h>
X#if defined(BSD42) || defined(BSD43)
X#include <strings.h>
X#else
X#ifndef SYSIII
X#include <string.h>
X#endif
X#endif
X
X#include <curses.h>
X#if defined(BSD42) || defined(BSD43)
X#include <sys/file.h>
X#else
X#include <fcntl.h>
X#endif
X#include "sc.h"
X#include <signal.h>
X#include <errno.h>
X
X#ifdef SYSV3
Xextern void exit();
X#else
Xextern int exit();
X#endif
X
Xvoid	openrow();
Xvoid	syncref();
Xvoid	unspecial();
X
Xextern	int	errno;
X
X/* a linked list of free [struct ent]'s, uses .next as the pointer */
Xextern	struct ent *freeents;
X
X/* a linked list of free [struct enodes]'s, uses .e.o.left as the pointer */
Xextern	struct enode *freeenodes;
X
X#define DEFCOLDELIM ':'
X
X/* copy the current row (currow) and place the cursor in the new row */
Xvoid
Xduprow()
X{
X    if (currow >= maxrows - 1 || maxrow >= maxrows - 1) {
X	if (!growtbl(GROWROW, 0, 0))
X		return;
X    }
X    modflg++;
X    currow++;
X    openrow (currow);
X    for (curcol = 0; curcol <= maxcol; curcol++) {
X	register struct ent *p = *ATBL(tbl, currow - 1, curcol);
X	if (p) {
X	    register struct ent *n;
X	    n = lookat (currow, curcol);
X	    (void)copyent ( n, p, 1, 0);
X	}
X    }
X    for (curcol = 0; curcol <= maxcol; curcol++) {
X	register struct ent *p = *ATBL(tbl, currow, curcol);
X	if (p && (p -> flags & is_valid) && !p -> expr)
X	    break;
X    }
X    if (curcol > maxcol)
X	curcol = 0;
X}
X
X/* copy the current column (curcol) and place the cursor in the new column */
Xvoid
Xdupcol() 
X{
X    if (curcol >= maxcols - 1 || maxcol >= maxcols - 1) {
X	if (!growtbl(GROWCOL, 0, 0))
X		return;
X    }
X    modflg++;
X    curcol++;
X    opencol (curcol, 1);
X    for (currow = 0; currow <= maxrow; currow++) {
X	register struct ent *p = *ATBL(tbl, currow, curcol - 1);
X	if (p) {
X	    register struct ent *n;
X	    n = lookat (currow, curcol);
X	    copyent ( n, p, 0, 1);
X	}
X    }
X    for (currow = 0; currow <= maxrow; currow++) {
X	register struct ent *p = *ATBL(tbl, currow, curcol);
X	if (p && (p -> flags & is_valid) && !p -> expr)
X	    break;
X    }
X    if (currow > maxrow)
X	currow = 0;
X}
X
X/* insert 'arg' rows before currow */
Xvoid
Xinsertrow(arg)
Xregister int arg;
X{
X    while (--arg>=0) openrow (currow);
X}
X
X/* delete 'arg' rows starting at currow (deletes from currow downward) */
Xvoid
Xdeleterow(arg)
Xregister int arg;
X{
X    flush_saved();
X    erase_area(currow, 0, currow + arg - 1, maxcol);
X    currow += arg;
X    while (--arg>=0) closerow (--currow);
X    sync_refs();
X}
X
Xvoid
Xerase_area(sr, sc, er, ec)
Xint sr, sc, er, ec;
X{
X    register int r, c;
X    register struct ent **pp;
X
X    if (sr > er) {
X	r = sr; sr = er; er= r;	
X    }
X
X    if (sc > ec) {
X	c = sc; sc = ec; ec= c;	
X    }
X
X    if (sr < 0)
X	sr = 0; 
X    if (sc < 0)
X	sc = 0;
X    checkbounds(&er, &ec);
X
X    for (r = sr; r <= er; r++) {
X	for (c = sc; c <= ec; c++) {
X	    pp = ATBL(tbl, r, c);
X	    if (*pp) {
X		free_ent(*pp);
X		*pp = (struct ent *)0;
X	    }
X	}
X    }
X}
X
X/*
X * deletes the expression associated w/ a cell and turns it into a constant
X * containing whatever was on the screen
X */
Xvoid
Xvalueize_area(sr, sc, er, ec)
Xint sr, sc, er, ec;
X{
X    register int r, c;
X    register struct ent *p;
X
X    if (sr > er) {
X	r = sr; sr = er; er= r;	
X    }
X
X    if (sc > ec) {
X	c = sc; sc = ec; ec= c;	
X    }
X
X    if (sr < 0)
X	sr = 0; 
X    if (sc < 0)
X	sc = 0;
X    checkbounds(&er, &ec);
X
X    for (r = sr; r <= er; r++) {
X	for (c = sc; c <= ec; c++) {
X	    p = *ATBL(tbl, r, c);
X	    if (p && p->expr) {
X		efree(p->expr);
X		p->expr = (struct enode *)0;
X		p->flags &= ~is_strexpr;
X	    }
X	}
X    }
X
X}
X
Xvoid
Xpullcells(to_insert)
Xint to_insert;
X{
X    register struct ent *p, *n;
X    register int deltar, deltac;
X    int minrow, mincol;
X    int mxrow, mxcol;
X    int numrows, numcols;
X
X    if (! to_fix)
X    {
X	error ("No data to pull");
X	return;
X    }
X
X    minrow = maxrows; 
X    mincol = maxcols;
X    mxrow = 0;
X    mxcol = 0;
X
X    for (p = to_fix; p; p = p->next) {
X	if (p->row < minrow)
X	    minrow = p->row;
X	if (p->row > mxrow)
X	    mxrow = p->row;
X	if (p->col < mincol)
X	    mincol = p->col;
X	if (p->col > mxcol)
X	    mxcol = p->col;
X    }
X
X    numrows = mxrow - minrow + 1;
X    numcols = mxcol - mincol + 1;
X    deltar = currow - minrow;
X    deltac = curcol - mincol;
X
X    if (to_insert == 'r') {
X	insertrow(numrows);
X	deltac = 0;
X    } else if (to_insert == 'c') {
X	opencol(curcol, numcols);
X	deltar = 0;
X    }
X
X    FullUpdate++;
X    modflg++;
X
X    for (p = to_fix; p; p = p->next) {
X	n = lookat (p->row + deltar, p->col + deltac);
X	(void) clearent(n);
X	copyent( n, p, deltar, deltac);
X	n -> flags = p -> flags & ~is_deleted;
X    }
X}
X
Xvoid
Xcolshow_op()
X{
X    register int i,j;
X    for (i=0; i<maxcols; i++)
X	if (col_hidden[i]) 
X	    break;
X    for(j=i; j<maxcols; j++)
X	if (!col_hidden[j])
X	    break;
X    j--;
X    if (i>=maxcols)
X	error ("No hidden columns to show");
X    else {
X	(void) sprintf(line,"show %s:", coltoa(i));
X	(void) sprintf(line + strlen(line),"%s",coltoa(j));
X	linelim = strlen (line);
X    }
X}
X
Xvoid
Xrowshow_op()
X{
X    register int i,j;
X    for (i=0; i<maxrows; i++)
X	if (row_hidden[i]) 
X	    break;
X    for(j=i; j<maxrows; j++)
X	if (!row_hidden[j]) {
X	    break;
X	}
X    j--;
X
X    if (i>=maxrows)
X	error ("No hidden rows to show");
X    else {
X	(void) sprintf(line,"show %d:%d", i, j);
X        linelim = strlen (line);
X    }
X}
X
X/*
X * Given a row/column command letter, emit a small menu, then read a qualifier
X * character for a row/column command and convert it to 'r' (row), 'c'
X * (column), or 0 (unknown).  If ch is 'p', an extra qualifier 'm' is allowed.
X */
X
Xint
Xget_rcqual (ch)
X    int ch;
X{
X    error ("%sow/column:  r: row  c: column%s",
X
X	    (ch == 'i') ? "Insert r" :
X	    (ch == 'a') ? "Append r" :
X	    (ch == 'd') ? "Delete r" :
X	    (ch == 'p') ? "Pull r" :
X	    (ch == 'v') ? "Values r" :
X	    (ch == 'z') ? "Zap r" :
X	    (ch == 's') ? "Show r" : "R",
X
X	    (ch == 'p') ? "  m: merge" : "");
X
X    (void) refresh();
X
X    switch (nmgetch())
X    {
X	case 'r':
X	case 'l':
X	case 'h':
X	case ctl('f'):
X	case ctl('b'):	return ('r');
X
X	case 'c':
X	case 'j':
X	case 'k':
X	case ctl('p'):
X	case ctl('n'):	return ('c');
X
X	case 'm':	return ((ch == 'p') ? 'm' : 0);
X
X	case ESC:
X	case ctl('g'):	return (ESC);
X
X	default:	return (0);
X    }
X    /*NOTREACHED*/
X}
X
Xvoid
Xopenrow (rs)
Xint	rs;
X{
X    register	r, c;
X    struct ent	**tmprow, **pp;
X
X    if (rs > maxrow) maxrow = rs;
X    if (maxrow >= maxrows - 1 || rs > maxrows - 1) {
X	if (!growtbl(GROWROW, rs, 0))
X		return;
X    }
X	/*
X	 * save the last active row+1, shift the rows downward, put the last
X	 * row in place of the first
X	 */
X    tmprow = tbl[++maxrow];
X    for (r = maxrow; r > rs; r--) {
X	row_hidden[r] = row_hidden[r-1];
X	tbl[r] = tbl[r-1];
X	pp = ATBL(tbl, r, 0);
X	for (c = 0; c < maxcols; c++, pp++)
X		if (*pp)
X			(*pp)->row = r;
X    }
X    tbl[r] = tmprow;	/* the last row was never used.... */
X    FullUpdate++;
X    modflg++;
X}
X
X/* delete row r */
Xvoid
Xcloserow (r)
Xregister r;
X{
X    register struct ent **pp;
X    register c;
X    struct ent	**tmprow;
X
X    if (r > maxrow) return;
X
X    /* save the row and empty it out */
X    tmprow = tbl[r];
X    pp = ATBL(tbl, r, 0);
X    for (c=maxcol+1; --c>=0; pp++) {
X	if (*pp)
X	{	free_ent(*pp);
X		*pp = (struct ent *)0;
X	}
X    }
X
X    /* move the rows, put the deleted, but now empty, row at the end */
X    for (; r < maxrows - 1; r++) {
X	row_hidden[r] = row_hidden[r+1];
X	tbl[r] = tbl[r+1];
X	pp = ATBL(tbl, r, 0);
X	for (c = 0; c < maxcols; c++, pp++)
X		if (*pp)
X			(*pp)->row = r;
X    }
X    tbl[r] = tmprow;
X
X    maxrow--;
X    FullUpdate++;
X    modflg++;
X}
X
Xvoid
Xopencol (cs, numcol)
Xint	cs;
Xint	numcol;
X{
X    register r;
X    register struct ent **pp;
X    register c;
X    register lim = maxcol-cs+1;
X    int i;
X
X    if (cs > maxcol)
X	maxcol = cs;
X    maxcol += numcol;
X
X    if ((maxcol >= maxcols - 1) && !growtbl(GROWCOL, 0, maxcol))
X		return;
X
X    for (i = maxcol; i > cs; i--) {
X	fwidth[i] = fwidth[i-numcol];
X	precision[i] = precision[i-numcol];
X	realfmt[i] = realfmt[i-numcol];
X	col_hidden[i] = col_hidden[i-numcol];
X    }
X    for (c = cs; c - cs < numcol; c++)
X    {	fwidth[c] = DEFWIDTH;
X	precision[c] =  DEFPREC;
X	realfmt[c] = DEFREFMT;
X    }
X	
X    for (r=0; r<=maxrow; r++) {
X	pp = ATBL(tbl, r, maxcol);
X	for (c=lim; --c>=0; pp--)
X	    if (pp[0] = pp[-numcol])
X		pp[0]->col += numcol;
X
X	pp = ATBL(tbl, r, cs);
X	for (c = cs; c - cs < numcol; c++, pp++)
X		*pp = (struct ent *)0;
X    }
X    FullUpdate++;
X    modflg++;
X}
X
Xvoid
Xclosecol (cs, numcol)
Xint cs;
Xint	numcol;
X{
X    register r;
X    register struct ent **pp;
X    register struct ent *q;
X    register c;
X    register lim = maxcol-cs;
X    int i;
X    char buf[50];
X
X    if (lim - numcol < -1)
X    {	(void) sprintf(buf, "Can't delete %d column%s %d columns left", numcol,
X			(numcol > 1 ? "s," : ","), lim+1);
X	error(buf);
X	return;
X    }
X    flush_saved();
X    erase_area(0, curcol, maxrow, curcol + numcol - 1);
X    sync_refs();
X
X    /* clear then copy the block left */
X    lim = maxcols - numcol - 1;
X    for (r=0; r<=maxrow; r++) {
X	for (c = cs; c - cs < numcol; c++)
X		if (q = *ATBL(tbl, r, c))
X			free_ent(q);
X
X	pp = ATBL(tbl, r, cs);
X	for (c=cs; c <= lim; c++, pp++)
X	{   if (c > lim)
X		*pp = (struct ent *)0;
X	    else
X	    if (pp[0] = pp[numcol])
X		pp[0]->col -= numcol;
X	}
X
X	c = numcol;
X	for (; --c >= 0; pp++)		
X		*pp = (struct ent *)0;
X    }
X
X    for (i = cs; i < maxcols - numcol - 1; i++) {
X	fwidth[i] = fwidth[i+numcol];
X	precision[i] = precision[i+numcol];
X	realfmt[i] = realfmt[i+numcol];
X	col_hidden[i] = col_hidden[i+numcol];
X    }
X    for (; i < maxcols - 1; i++) {
X	fwidth[i] = DEFWIDTH;
X	precision[i] = DEFPREC;
X	realfmt[i] = DEFREFMT;
X	col_hidden[i] = 0;
X    }
X
X    maxcol -= numcol;
X    FullUpdate++;
X    modflg++;
X}
X
Xvoid
Xdoend(rowinc, colinc)
Xint rowinc, colinc;
X{
X    register struct ent *p;
X    int r, c;
X
X    if (VALID_CELL(p, currow, curcol)) {
X	r = currow + rowinc;
X	c = curcol + colinc;
X	if (r >= 0 && r < maxrows && 
X	    c >= 0 && c < maxcols &&
X	    !VALID_CELL(p, r, c)) {
X		currow = r;
X		curcol = c;
X	}
X    }
X
X    if (!VALID_CELL(p, currow, curcol)) {
X        switch (rowinc) {
X        case -1:
X	    while (!VALID_CELL(p, currow, curcol) && currow > 0)
X		currow--;
X	    break;
X        case  1:
X	    while (!VALID_CELL(p, currow, curcol) && currow < maxrows-1)
X		currow++;
X	    break;
X        case  0:
X            switch (colinc) {
X 	    case -1:
X	        while (!VALID_CELL(p, currow, curcol) && curcol > 0)
X		    curcol--;
X	        break;
X 	    case  1:
X	        while (!VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
X		    curcol++;
X	        break;
X	    }
X            break;
X        }
X
X	error ("");	/* clear line */
X	return;
X    }
X
X    switch (rowinc) {
X    case -1:
X	while (VALID_CELL(p, currow, curcol) && currow > 0)
X	    currow--;
X	break;
X    case  1:
X	while (VALID_CELL(p, currow, curcol) && currow < maxrows-1)
X	    currow++;
X	break;
X    case  0:
X	switch (colinc) {
X	case -1:
X	    while (VALID_CELL(p, currow, curcol) && curcol > 0)
X		curcol--;
X	    break;
X	case  1:
X	    while (VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
X		curcol++;
X	    break;
X	}
X	break;
X    }
X    if (!VALID_CELL(p, currow, curcol)) {
X        currow -= rowinc;
X        curcol -= colinc;
X    }
X}
X
X/* Modified 9/17/90 THA to handle more formats */
Xvoid
Xdoformat(c1,c2,w,p,r)
Xint c1,c2,w,p,r;
X{
X    register int i;
X    int crows = 0;
X    int ccols = c2;
X
X    if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ;
X    if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ;
X
X    if (w > COLS - RESCOL - 2) {
X	error("Format too large - Maximum = %d", COLS - RESCOL - 2);
X	w = COLS-RESCOL-2;
X    }
X
X    if (p > w) {
X	error("Precision too large");
X	p = w;
X    }
X    /* format statement may record format of an as yet unused column
X     * which causes it to run off the end of fwidth and precision --
X     * causing a bus error later
X     */
X    checkbounds(&crows, &ccols);
X    if (ccols < c2) {
X	error("Format statement failed to create implied column %d", c2);
X	return;
X    }
X
X    for(i = c1; i<=c2; i++)
X	fwidth[i] = w, precision[i] = p, realfmt[i] = r;
X
X    FullUpdate++;
X    modflg++;
X}
X
Xvoid
Xprint_options(f)
XFILE *f;
X{
X    if(
X       autocalc &&
X       propagation == 10 &&
X       calc_order == BYROWS &&
X       !numeric &&
X       prescale == 1.0 &&
X       !extfunc &&
X       showcell &&
X       showtop &&
X       tbl_style == 0
X      )
X		return;		/* No reason to do this */
X
X    (void) fprintf(f, "set");
X    if(!autocalc) 
X	(void) fprintf(f," !autocalc");
X    if(propagation != 10)
X	(void) fprintf(f, " iterations = %d", propagation);
X    if(calc_order != BYROWS )
X	(void) fprintf(f, " bycols");
X    if (numeric)
X	(void) fprintf(f, " numeric");
X    if (prescale != 1.0)
X	(void) fprintf(f, " prescale");
X    if (extfunc)
X	(void) fprintf(f, " extfun");
X    if (!showcell)
X	(void) fprintf(f, " !cellcur");
X    if (!showtop)
X	(void) fprintf(f, " !toprow");
X    if (tbl_style)
X	(void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" :
X					tbl_style == LATEX ? "latex" :
X					tbl_style == SLATEX ? "slatex" :
X					tbl_style == TEX ? "tex" : "0" );
X    (void) fprintf(f, "\n");
X}
X
Xvoid
Xprintfile (fname, r0, c0, rn, cn)
Xchar *fname;
Xint r0, c0, rn, cn;
X{
X    FILE *f;
X    static char *pline = NULL;		/* only malloc once, malloc is slow */
X    static unsigned fbufs_allocated = 0;
X    int plinelim;
X    int pid;
X    int fieldlen, nextcol;
X    register row, col;
X    register struct ent **pp;
X    char *xmalloc();
X    char *xrealloc();
X
X    if ((strcmp(fname, curfile) == 0) &&
X	!yn_ask("Confirm that you want to destroy the data base: (y,n)")) {
X	return;
X    }
X
X    if (!pline && (pline = xmalloc((unsigned)(FBUFLEN *
X					++fbufs_allocated))) == (char *)NULL)
X    {   error("Malloc failed in printfile()");
X        return;
X    }
X
X    if ((f = openout(fname, &pid)) == (FILE *)0)
X    {	error ("Can't create file \"%s\"", fname);
X	return;
X    }
X    for (row=r0;row<=rn; row++) {
X	register c = 0;
X
X	if (row_hidden[row])
X	    continue;
X
X	pline[plinelim=0] = '\0';
X	for (pp = ATBL(tbl, row, col=c0); col<=cn;
X	        pp += nextcol-col, col = nextcol, c += fieldlen) {
X
X	    nextcol = col+1;
X	    if (col_hidden[col]) {
X		fieldlen = 0;
X		continue;
X	    }
X
X	    fieldlen = fwidth[col];
X	    if (*pp) {
X		char *s;
X
X		/* 
X		 * dynamically allocate pline, making sure we are not 
X		 * attempting to write 'out of bounds'.
X		 */
X		while(c > (fbufs_allocated * FBUFLEN)) {
X		  if((pline = xrealloc
X			       ((char *)pline, 
X				(unsigned)(FBUFLEN * ++fbufs_allocated)))
X		     == NULL) {
X		    error ("Realloc failed in printfile()");
X		    return;
X		  }
X		}		  
X		while (plinelim<c) pline[plinelim++] = ' ';
X		plinelim = c;
X		if ((*pp)->flags&is_valid) {
X		    while(plinelim + fwidth[col] > 
X			  (fbufs_allocated * FBUFLEN)) {
X		      if((pline = ((char *)xrealloc
X				   ((char *)pline, 
X				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
X			 == NULL) {
X			error ("Realloc failed in printfile()");
X			return;
X		      }
X		    }
X		    if ((*pp)->cellerror)
X			(void) sprintf (pline+plinelim, "%*s",
X				fwidth[col],
X			((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
X		    else
X		    {
X		      if ((*pp)->format) {
X	   	        char field[FBUFLEN];
X			format ((*pp)->format, (*pp)->v, field,
X				       sizeof(field));
X			(void) sprintf (pline+plinelim, "%*s", fwidth[col],
X					field);
X		      } else {
X	   	        char field[FBUFLEN];
X			(void) engformat(realfmt[col], fwidth[col],
X                                             precision[col], (*pp) -> v,
X                                             field, sizeof(field));
X			(void) sprintf (pline+plinelim, "%*s", fwidth[col],
X				       field);
X		      }
X		    }
X		    plinelim += strlen (pline+plinelim);
X		}
X		if (s = (*pp)->label) {
X		    int slen;
X		    char *start, *last;
X		    register char *fp;
X		    struct ent *nc;
X
X		    /* Figure out if the label slops over to a blank field */
X		    slen = strlen(s);
X		    while (slen > fieldlen && nextcol <= cn &&
X			    !((nc = lookat(row,nextcol))->flags & is_valid) &&
X			    !(nc->label)) {
X			
X	                if (!col_hidden[nextcol])
X		 	    fieldlen += fwidth[nextcol];
X
X			nextcol++;
X		    }
X		    if (slen > fieldlen)
X			slen = fieldlen;
X		    
X		    while(c + fieldlen + 2 > (fbufs_allocated * FBUFLEN)) {
X		      if((pline = ((char *)xrealloc
X				   ((char *)pline, 
X				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
X			 == NULL) {
X			error ("xrealloc failed in printfile()");
X			return;
X		      }
X		    }		  
X
X		    /* Now justify and print */
X		    start = (*pp)->flags & is_leftflush ? pline + c
X					: pline + c + fieldlen - slen;
X		    last = pline + c + fieldlen;
X		    fp = plinelim < c ? pline + plinelim : pline + c;
X		    while (fp < start)
X			*fp++ = ' ';
X		    while (slen--)
X			*fp++ = *s++;
X		    if (!((*pp)->flags & is_valid) || fieldlen != fwidth[col])
X			while(fp < last)
X			    *fp++ = ' ';
X		    if (plinelim < fp - pline)
X			plinelim = fp - pline;
X		}
X	    }
X	}
X	pline[plinelim++] = '\n';
X	pline[plinelim] = '\0';
X	(void) fputs (pline, f);
X    }
X
X    closeout(f, pid);
X}
X
Xvoid
Xtblprintfile (fname, r0, c0, rn, cn)
Xchar *fname;
Xint r0, c0, rn, cn;
X{
X    FILE *f;
X    int pid;
X    register row, col;
X    register struct ent **pp;
X    char coldelim = DEFCOLDELIM;
X
X    if ((strcmp(fname, curfile) == 0) &&
X	!yn_ask("Confirm that you want to destroy the data base: (y,n)"))
X	    return;
X
X    if ((f = openout(fname, &pid)) == (FILE *)0)
X    {	error ("Can't create file \"%s\"", fname);
X	return;
X    }
X
X    if ( tbl_style == TBL ) {
X	fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname);
X	fprintf(f,"tab(%c);\n",coldelim);
X	for (col=c0;col<=cn; col++) fprintf(f," n");
X	fprintf(f, ".\n");
X    }
X    else if ( tbl_style == LATEX ) {
X	fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname);
X	for (col=c0;col<=cn; col++) fprintf(f,"c");
X	fprintf(f, "}\n");
X	coldelim = '&';
X    }
X    else if ( tbl_style == SLATEX ) {
X	fprintf(f,"%% ** %s spreadsheet output\n!begin<tabular><",progname);
X	for (col=c0;col<=cn; col++) fprintf(f,"c");
X	fprintf(f, ">\n");
X	coldelim = '&';
X    }
X    else if ( tbl_style == TEX ) {
X	fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n",
X		progname, cn-c0+1);
X	coldelim = '&';
X    }
X
X    for (row=r0; row<=rn; row++) {
X	if ( tbl_style == TEX )
X	    (void) fprintf (f, "\\+");
X	
X	for (pp = ATBL(tbl, row, col=c0); col<=cn; col++, pp++) {
X	    if (*pp) {
X		char *s;
X		if ((*pp)->flags&is_valid) {
X		    if ((*pp)->cellerror) {
X			(void) fprintf (f, "%*s",
X					fwidth[col],
X			((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
X		    }
X		    else
X		    if ((*pp)->format) {
X		        char field[FBUFLEN];
X			
X			(void) format ((*pp)->format, (*pp)->v, field,
X				       sizeof(field));
X			unspecial (f, field, coldelim);
X		    } else {
X		        char field[FBUFLEN];
X                        (void) engformat(realfmt[col], fwidth[col],
X                                             precision[col], (*pp) -> v,
X                                             field, sizeof(field));
X			unspecial (f, field, coldelim);
X		    }
X		}
X		if (s = (*pp)->label) {
X	            unspecial (f, s, coldelim);
X		}
X	    }
X	    if ( col < cn )
X		(void) fprintf(f,"%c", coldelim);
X	}
X	if ( tbl_style == LATEX ) {
X		if ( row < rn ) (void) fprintf (f, "\\\\");
X	}
X	else if ( tbl_style == SLATEX ) {
X		if ( row < rn ) (void) fprintf (f, "!!");
X	}
X	else if ( tbl_style == TEX ) {
X		(void) fprintf (f, "\\cr");
X	}
X	(void) fprintf (f,"\n");
X    }
X
X    if ( tbl_style == TBL )
X    (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname);
X    else if ( tbl_style == LATEX )
X    (void) fprintf (f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname);
X    else if ( tbl_style == SLATEX )
X    (void) fprintf (f,"!end<tabular>\n%% ** end of %s spreadsheet output\n", progname);
X    else if ( tbl_style == TEX )
X    (void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname);
X
X    closeout(f, pid);
X}
X
X/* unspecial (backquote) things that are special chars in a table */
Xvoid
Xunspecial(f, str, delim)
XFILE	*f;
Xchar	*str;
Xint	delim;
X{
X	while (*str)
X	{	if (((tbl_style == LATEX) || (tbl_style == SLATEX) ||
X		    (tbl_style == TEX)) &&
X		    ((*str == delim) || (*str == '$') || (*str == '#') ||
X		    (*str == '%') || (*str == '{') || (*str == '}') ||
X		    (*str == '[') || (*str == ']') || (*str == '&')))
X			putc('\\', f);
X		putc(*str, f);
X		str++;
X	}
X}
X
Xstruct enode *
Xcopye (e, Rdelta, Cdelta)
Xregister struct enode *e;
Xint Rdelta, Cdelta;
X{
X    register struct enode *ret;
X
X    if (e == (struct enode *)0) {
X        ret = (struct enode *)0;
X    } else if (e->op & REDUCE) {
X	int newrow, newcol;
X	if (freeenodes)
X	{	ret = freeenodes;
X		freeenodes = ret->e.o.left;
X	}
X	else
X		ret = (struct enode *) xmalloc ((unsigned) sizeof (struct enode));
X	ret->op = e->op;
X	newrow=e->e.r.left.vf & FIX_ROW ? e->e.r.left.vp->row :
X					  e->e.r.left.vp->row+Rdelta;
X	newcol=e->e.r.left.vf & FIX_COL ? e->e.r.left.vp->col :
X					  e->e.r.left.vp->col+Cdelta;
X	ret->e.r.left.vp = lookat (newrow, newcol);
X	ret->e.r.left.vf = e->e.r.left.vf;
X	newrow=e->e.r.right.vf & FIX_ROW ? e->e.r.right.vp->row :
X					   e->e.r.right.vp->row+Rdelta;
X	newcol=e->e.r.right.vf & FIX_COL ? e->e.r.right.vp->col :
X					   e->e.r.right.vp->col+Cdelta;
X	ret->e.r.right.vp = lookat (newrow, newcol);
X	ret->e.r.right.vf = e->e.r.right.vf;
X    } else {
X	if (freeenodes)
X	{	ret = freeenodes;
X		freeenodes = ret->e.o.left;
X	}
X	else
X		ret = (struct enode *) xmalloc ((unsigned) sizeof (struct enode));
X	ret->op = e->op;
X	switch (ret->op) {
X	case 'v':
X		{
X		    int newrow, newcol;
X		    newrow=e->e.v.vf & FIX_ROW ? e->e.v.vp->row :
X						 e->e.v.vp->row+Rdelta;
X		    newcol=e->e.v.vf & FIX_COL ? e->e.v.vp->col :
X						 e->e.v.vp->col+Cdelta;
X		    ret->e.v.vp = lookat (newrow, newcol);
X		    ret->e.v.vf = e->e.v.vf;
X		    break;
X		}
X	case 'k':
X		ret->e.k = e->e.k;
X		break;
X	case 'f':
X		ret->e.o.right = copye (e->e.o.right,0,0);
X		ret->e.o.left = (struct enode *)0;
X 		break;
X	case '$':
X		ret->e.s = xmalloc((unsigned) strlen(e->e.s)+1);
X		(void) strcpy(ret->e.s, e->e.s);
X		break;
X	default:
X		ret->e.o.right = copye (e->e.o.right,Rdelta,Cdelta);
X		ret->e.o.left = copye (e->e.o.left,Rdelta,Cdelta);
X		break;
X	}
X    }
X    return ret;
X}
X
X/*
X
X * sync_refs and syncref are used to remove references to
X * deleted struct ents.  Note that the deleted structure must still
X * be hanging around before the call, but not referenced by an entry
X * in tbl.  Thus the free_ent, fix_ent calls in sc.c
X */
Xvoid
Xsync_refs ()
X{
X    register i,j;
X    register struct ent *p;
X    sync_ranges();
X    for (i=0; i<=maxrow; i++)
X	for (j=0; j<=maxcol; j++)
X	    if ((p = *ATBL(tbl, i, j)) && p->expr)
X		syncref(p->expr);
X}
X
Xvoid
Xsyncref(e)
Xregister struct enode *e;
X{
X    if (e == (struct enode *)0)
X	return;
X    else if (e->op & REDUCE) {
X 	e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col);
X 	e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col);
X    } else {
X	switch (e->op) {
X	case 'v':
X		e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col);
X		break;
X	case 'k':
X		break;
X	case '$':
X		break;
X	default:
X		syncref(e->e.o.right);
X		syncref(e->e.o.left);
X		break;
X	}
X    }
X}
X
X/* mark a row as hidden */
Xvoid
Xhiderow(arg)
Xint arg;
X{
X    register int r1;
X    register int r2;
X
X    r1 = currow;
X    r2 = r1 + arg - 1;
X    if (r1 < 0 || r1 > r2) {
X	error ("Invalid range");
X	return;
X    }
X    if (r2 >= maxrows-1)
X    {	if (!growtbl(GROWROW, arg+1, 0))
X	{	error("You can't hide the last row");
X		return;
X	}
X    }
X    FullUpdate++;
X    modflg++;
X    while (r1 <= r2)
X	row_hidden[r1++] = 1;
X}
X
X/* mark a column as hidden */
Xvoid
Xhidecol(arg)
Xint arg;
X{
X    register int c1;
X    register int c2;
X
X    c1 = curcol;
X    c2 = c1 + arg - 1;
X    if (c1 < 0 || c1 > c2) {
X	error ("Invalid range");
X	return;
X    }
X    if (c2 >= maxcols-1)
X    {	if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1))
X	{	error("You can't hide the last col");
X		return;
X	}
X    }
X    FullUpdate++;
X    modflg++;
X    while (c1 <= c2)
X	col_hidden[c1++] = 1;
X}
X
X/* mark a row as not-hidden */
Xvoid
Xshowrow(r1, r2)
Xint r1, r2;
X{
X    if (r1 < 0 || r1 > r2) {
X	error ("Invalid range");
X	return;
X    }
X    if (r2 > maxrows-1) {
X	r2 = maxrows-1;
X    }
X    FullUpdate++;
X    modflg++;
X    while (r1 <= r2)
X	row_hidden[r1++] = 0;
X}
X
X/* mark a column as not-hidden */
Xvoid
Xshowcol(c1, c2)
Xint c1, c2;
X{
X    if (c1 < 0 || c1 > c2) {
X	error ("Invalid range");
X	return;
X    }
X    if (c2 > maxcols-1) {
X	c2 = maxcols-1;
X    }
X    FullUpdate++;
X    modflg++;
X    while (c1 <= c2)
X	col_hidden[c1++] = 0;
X}
X
X/* Open the output file, setting up a pipe if needed */
XFILE *
Xopenout(fname, rpid)
Xchar *fname;
Xint *rpid;
X{
X    int pipefd[2];
X    int pid;
X    FILE *f;
X    char *efname;
X
X    while (*fname && (*fname == ' '))  /* Skip leading blanks */
X	fname++;
X
X    if (*fname != '|') {		/* Open file if not pipe */
X	*rpid = 0;
X	
X	efname = findhome(fname);
X#ifdef DOBACKUPS
X	if (!backup_file(efname) &&
X	    (yn_ask("Could not create backup copy, Save anyhow?: (y,n)") != 1))
X		return(0);
X#endif
X	return(fopen(efname, "w"));
X    }
X
X    fname++;				/* Skip | */
X    if ( pipe (pipefd) < 0) {
X	error("Can't make pipe to child");
X	*rpid = 0;
X	return(0);
X    }
X
X    deraw();
X#ifdef VMS
X    fprintf(stderr, "No son tasks available yet under VMS--sorry\n");
X#else /* VMS */
X
X    if ((pid=fork()) == 0)			  /* if child  */
X    {
X	(void) close (0);			  /* close stdin */
X	(void) close (pipefd[1]);
X	(void) dup (pipefd[0]);		  /* connect to pipe input */
X	(void) signal (SIGINT, SIG_DFL);	  /* reset */
X	(void) execl ("/bin/sh", "sh", "-c", fname, 0);
X	exit (-127);
X    }
X    else				  /* else parent */
X    {
X	*rpid = pid;
X	if ((f = fdopen (pipefd[1], "w")) == (FILE *)0)
X	{
X	    (void) kill (pid, -9);
X	    error ("Can't fdopen output");
X	    (void) close (pipefd[1]);
X	    *rpid = 0;
X	    return(0);
X	}
X    }
X#endif /* VMS */
X    return(f);
X}
X
X/* close a file opened by openout(), if process wait for return */
Xvoid
Xcloseout(f, pid)
XFILE *f;
Xint pid;
X{
X    int temp;
X
X    (void) fclose (f);
X    if (pid) {
X         while (pid != wait(&temp)) /**/;
X	 (void) printf("Press RETURN to continue ");
X	 (void) fflush(stdout);
X	 (void) nmgetch();
X	 goraw();
X    }
X}
X
Xvoid
Xcopyent(n,p,dr,dc)
X	    register struct ent *n, *p;
X	    int dr, dc;
X{
X    if(!n||!p){error("internal error");return;}
X    n -> v = p -> v;
X    n -> flags = p -> flags;
X    n -> expr = copye (p -> expr, dr, dc);
X    n -> label = (char *)0;
X    if (p -> label) {
X	n -> label = xmalloc ((unsigned) (strlen (p -> label) + 1));
X	(void) strcpy (n -> label, p -> label);
X    }
X    n -> format = 0;
X    if (p -> format) {
X        n -> format = xmalloc ((unsigned) (strlen (p -> format) + 1));
X	(void) strcpy (n -> format, p -> format);
X    }
X}
X
Xvoid
Xwrite_fd (f, r0, c0, rn, cn)
Xregister FILE *f;
Xint r0, c0, rn, cn;
X{
X    register struct ent **pp;
X    register r, c;
X
X    (void) fprintf (f, "# This data file was generated by the Spreadsheet ");
X    (void) fprintf (f, "Calculator.\n");
X    (void) fprintf (f, "# You almost certainly shouldn't edit it.\n\n");
X    print_options(f);
X    for (c=0; c<maxcols; c++)
X	if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC || realfmt[c] != DEFREFMT )
X	    (void) fprintf (f, "format %s %d %d %d\n",coltoa(c),fwidth[c],precision[c],realfmt[c]);
X    for (c=c0; c<cn; c++) {
X        if (col_hidden[c]) {
X            (void) fprintf(f, "hide %s\n", coltoa(c));
X        }
X    }
X    for (r=r0; r<=rn; r++) {
X	if (row_hidden[r]) {
X	    (void) fprintf(f, "hide %d\n", r);
X	}
X    }
X
X    write_range(f);
X
X    if (mdir) 
X	    (void) fprintf(f, "mdir \"%s\"\n", mdir);
X    for (r=r0; r<=rn; r++) {
X	pp = ATBL(tbl, r, c0);
X	for (c=c0; c<=cn; c++, pp++)
X	    if (*pp) {
X		if ((*pp)->label) {
X		    edits(r,c);
X		    (void) fprintf(f, "%s\n",line);
X		}
X		if ((*pp)->flags&is_valid) {
X		    editv (r, c);
X		    (void) fprintf (f, "%s\n",line);
X		}
X		if ((*pp)->format) {
X		    editfmt (r, c);
X		    (void) fprintf (f, "%s\n",line);
X		}
X	    }
X    }
X    if (rndinfinity)
X	fprintf(f, "set rndinfinity\n");
X}
X
Xint
Xwritefile (fname, r0, c0, rn, cn)
Xchar *fname;
Xint r0, c0, rn, cn;
X{
X    register FILE *f;
X    char save[PATHLEN];
X    int pid;
X
X#ifndef VMS
X    if (Crypt) {
X	return (cwritefile(fname, r0, c0, rn, cn));
X    }
X#endif /* VMS */
X
X    if (*fname == '\0') fname = curfile;
X
X    (void) strcpy(save,fname);
X
X    if ((f= openout(fname, &pid)) == (FILE *)0)
X    {	error ("Can't create file \"%s\"", fname);
X	return (-1);
X    }
X
X    write_fd(f, r0, c0, rn, cn);
X    
X    closeout(f, pid);
X
X    if (!pid) {
X        (void) strcpy(curfile, save);
X        modflg = 0;
X        error("File \"%s\" written.",curfile);
X    }
X
X    return (0);
X}
X
Xvoid
Xreadfile (fname,eraseflg)
Xchar *fname;
Xint eraseflg;
X{
X    register FILE *f;
X    char save[PATHLEN];
X    int tempautolabel;
X
X    tempautolabel = autolabel;		/* turn off auto label when */
X    autolabel = 0;			/* when reading a file  */
X
X    if (*fname == '*' && mdir) { 
X       (void) strcpy(save, mdir);
X       *fname = '/';
X       (void) strcat(save, fname);
X    } else {
X        if (*fname == '\0')
X	    fname = curfile;
X        (void) strcpy(save,fname);
X    }
X
X#ifndef VMS
X    if (Crypt)  {
X	creadfile(save, eraseflg);
X	return;
X    }
X#endif /* VMS */
X
X    if (eraseflg && strcmp(fname,curfile) && modcheck(" first")) return;
X
X    if ((f = fopen(findhome(save), "r")) == (FILE *)0)
X    {	error ("Can't read file \"%s\"", save);
X	return;
X    }
X
X    if (eraseflg) erasedb ();
X
X    loading++;
X    while (fgets(line, sizeof(line), f)) {
X	linelim = 0;
X	if (line[0] != '#') (void) yyparse ();
X    }
X    --loading;
X    (void) fclose (f);
X    linelim = -1;
X    modflg++;
X    if (eraseflg) {
X	(void) strcpy(curfile,save);
X	modflg = 0;
X    }
X    autolabel = tempautolabel;
X    EvalAll();
X}
X
X/* erase the database (tbl, etc.) */
Xvoid
Xerasedb ()
X{
X    register r, c;
X    for (c = 0; c<=maxcol; c++) {
X	fwidth[c] = DEFWIDTH;
X	precision[c] = DEFPREC;
X	realfmt[c] = DEFREFMT;
X    }
X
X    for (r = 0; r<=maxrow; r++) {
X	register struct ent **pp = ATBL(tbl, r, 0);
X	for (c=0; c++<=maxcol; pp++)
X	    if (*pp) {
X		if ((*pp)->expr)  efree ((*pp) -> expr);
X		if ((*pp)->label) xfree ((char *)((*pp) -> label));
X		(*pp)->next = freeents;	/* save [struct ent] for reuse */
X		freeents = *pp;
X		*pp = (struct ent *)0;
X	    }
X    }
X    maxrow = 0;
X    maxcol = 0;
X    clean_range();
X    FullUpdate++;
X}
X
X/* moves curcol back one displayed column */
Xvoid
Xbackcol(arg)
X	int arg;
X{
X    while (--arg>=0) {
X	if (curcol)
X	    curcol--;
X	else
X	    {error ("At column A"); break;}
X	while(col_hidden[curcol] && curcol)
X	    curcol--;
X    }
X}
X
X/* moves curcol forward one displayed column */
Xvoid
Xforwcol(arg)
X	int arg;
X{
X    while (--arg>=0) {
X	if (curcol < maxcols - 1)
X	    curcol++;
X	else
X	if (!growtbl(GROWCOL, 0, arg))	/* get as much as needed */
X		break;
X	else
X		curcol++;
X	while(col_hidden[curcol]&&(curcol<maxcols-1))
X	    curcol++;
X    }
X}
X
X/* moves currow forward one displayed row */
Xvoid
Xforwrow(arg)
X	int arg;
X{
X    while (--arg>=0) {
X	if (currow < maxrows - 1)
X	    currow++;
X	else
X	if (!growtbl(GROWROW, arg, 0))	/* get as much as needed */
X		break;
X	else
X		currow++;
X	while (row_hidden[currow]&&(currow<maxrows-1))
X	    currow++;
X    }
X}
X
X/* moves currow backward one displayed row */
Xvoid
Xbackrow(arg)
X	int arg;
X{
X    while (--arg>=0) {
X	if (currow)
X	    currow--;
X	else
X	    {error ("At row zero"); break;}
X	while (row_hidden[currow] && currow)
X	    currow--;
X    }
X}
X
X
X/*
X * Show a cell's label string or expression value.  May overwrite value if
X * there is one already displayed in the cell.  Created from old code in
X * update(), copied with minimal changes.
X */
X
Xvoid
Xshowstring (string, leftflush, hasvalue, row, col, nextcolp, mxcol, fieldlenp, r, c)
X    char *string;	/* to display */
X    int leftflush;	/* or rightflush */
X    int hasvalue;	/* is there a numeric value? */
X    int row, col;	/* spreadsheet location */
X    int *nextcolp;	/* value returned through it */
X    int mxcol;		/* last column displayed? */
X    int *fieldlenp;	/* value returned through it */
X    int r, c;		/* screen row and column */
X{
X    register int nextcol  = *nextcolp;
X    register int fieldlen = *fieldlenp;
X
X    char field[FBUFLEN];
X    int  slen;
X    char *start, *last;
X    register char *fp;
X    struct ent *nc;
X
X    /* This figures out if the label is allowed to
X       slop over into the next blank field */
X
X    slen = strlen (string);
X    while ((slen > fieldlen) && (nextcol <= mxcol) &&
X	   !((nc = lookat (row, nextcol)) -> flags & is_valid) &&
X	   !(nc->label)) {
X
X	if (! col_hidden [nextcol])
X	    fieldlen += fwidth [nextcol];
X
X	nextcol++;
X    }
X    if (slen > fieldlen)
X	slen = fieldlen;
X
X    /* Now justify and print */
X    start = leftflush ? field : field + fieldlen - slen;
X    last = field+fieldlen;
X    fp = field;
X    while (fp < start)
X	*fp++ = ' ';
X    while (slen--)
X	*fp++ = *string++;
X    if ((! hasvalue) || fieldlen != fwidth[col]) 
X	while (fp < last)
X	    *fp++ = ' ';
X    *fp = '\0';
X#ifdef VMS
X    mvaddstr(r, c, field);	/* this is a macro */
X#else
X    (void) mvaddstr(r, c, field);
X#endif
X
X    *nextcolp  = nextcol;
X    *fieldlenp = fieldlen;
X}
X
Xint
Xetype(e)
Xregister struct enode *e;
X{
X    if (e == (struct enode *)0)
X	return NUM;
X    switch (e->op) {
X    case O_SCONST: case '#': case DATE: case FMT: case STINDEX:
X    case EXT: case SVAL: case SUBSTR:
X        return (STR);
X
X    case '?':
X    case IF:
X        return(etype(e->e.o.right->e.o.left));
X
X    case 'f':
X        return(etype(e->e.o.right));
X
X    case O_VAR: {
X	register struct ent *p;
X	p = e->e.v.vp;
X	if (p->expr) 
X	    return(p->flags & is_strexpr ? STR : NUM);
X	else if (p->label)
X	    return(STR);
X	else
X	    return(NUM);
X	}
X
X    default:
X	return(NUM);
X    }
X}
X
X/* return 1 if yes given, 0 otherwise */
Xint
Xyn_ask(msg)
Xchar	*msg;
X{	char ch;
X
X	(void) move (0, 0);
X	(void) clrtoeol ();
X	(void) addstr (msg);
X	(void) refresh();
X	ch = nmgetch();
X	if ( ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N' ) {
X		if (ch == ctl('g') || ch == ESC)
X			return(-1);
X		error("y or n response required");
X		return (-1);
X	}
X	if (ch == 'y' || ch == 'Y')
X		return(1);
X	else
X		return(0);
X}
X
X/* expand a ~ in a path to your home directory */
X#include <pwd.h>
Xchar	*
Xfindhome(path)
Xchar	*path;
X{
X	static	char	*HomeDir = NULL;
X	extern	char	*getenv();
X
X	if (*path == '~')
X	{	char	*pathptr;
X		char	tmppath[PATHLEN];
X
X		if (HomeDir == NULL)
X		{	HomeDir = getenv("HOME");
X			if (HomeDir == NULL)
X				HomeDir = "/";
X		}
X		pathptr = path + 1;
X		if ((*pathptr == '/') || (*pathptr == '\0'))
X		{	strcpy(tmppath, HomeDir);
X		}
X		else
X		{	struct	passwd *pwent;
X			extern	struct	passwd *getpwnam();
X			char	*namep;
X			char	name[50];
X
X			namep = name;
X			while ((*pathptr != '\0') && (*pathptr != '/'))
X				*(namep++) = *(pathptr++);
X			*namep = '\0';
X			if ((pwent = getpwnam(name)) == NULL)
X			{	(void) sprintf(path, "Can't find user %s", name);
X				return(NULL);
X			}
X			strcpy(tmppath, pwent->pw_dir);
X		}
X
X		strcat(tmppath, pathptr);
X		strcpy(path, tmppath);
X	}
X	return(path);
X}
X
X#ifdef DOBACKUPS
X#include <sys/stat.h>
X
X/*
X * make a backup copy of a file, use the same mode and name in the format
X * [path/]#file~
X * return 1 if we were successful, 0 otherwise
X */
Xint
Xbackup_file(path)
Xchar	*path;
X{
X	struct	stat	statbuf;
X	char	fname[PATHLEN];
X	char	tpath[PATHLEN];
X#ifdef sequent
X	static	char	*buf = NULL;
X	static	unsigned buflen = 0;
X#else
X	char	buf[BUFSIZ];
X#endif
X	char	*tpp;
X	int	infd, outfd;
X	int	count;
X
X	/* tpath will be the [path/]file ---> [path/]#file~ */
X	strcpy(tpath, path);
X	if ((tpp = strrchr(tpath, '/')) == NULL)
X		tpp = tpath;
X	else
X		tpp++;
X	strcpy(fname, tpp);
X	(void) sprintf(tpp, "#%s~", fname);
X
X	if (stat(path, &statbuf) == 0)
X	{
X		/* if we know the optimum block size, use it */
X#ifdef sequent
X		if ((statbuf.st_blksize > buflen) || (buf == NULL))
X		{	buflen = statbuf.st_blksize;
X			if ((buf = xrealloc(buf, buflen)) == (char *)0)
X			{	buflen = 0;
X				return(0);
X			}
X		}
X#endif
X
X		if ((infd = open(path, O_RDONLY, 0)) < 0)
X			return(0);
X
X		if ((outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT,
X					statbuf.st_mode)) < 0)
X			return(0);
X
X#ifdef sequent
X		while((count = read(infd, buf, statbuf.st_blksize)) > 0)
X#else
X		while((count = read(infd, buf, sizeof(buf))) > 0)
X#endif
X		{	if (write(outfd, buf, count) != count)
X			{	count = -1;
X				break;
X			}
X		}
X		close(infd);
X		close(outfd);
X
X		return((count < 0) ? 0 : 1);
X	}
X	else
X	if (errno == ENOENT)
X		return(1);
X	return(0);
X}
X#endif
END_OF_FILE
  if test 36608 -ne `wc -c <'cmds.c'`; then
    echo shar: \"'cmds.c'\" unpacked with wrong size!
  fi
  # end of 'cmds.c'
fi
if test -f 'sc.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sc.h'\"
else
  echo shar: Extracting \"'sc.h'\" \(9929 characters\)
  sed "s/^X//" >'sc.h' <<'END_OF_FILE'
X/*	SC	A Table Calculator
X *		Common definitions
X *
X *		original by James Gosling, September 1982
X *		modified by Mark Weiser and Bruce Israel,
X *			University of Maryland
X *		R. Bond  12/86
X *		More mods by Alan Silverstein, 3-4/88, see list of changes.
X *		$Revision: 6.16 $
X *
X */
X
X#define	ATBL(tbl, row, col)	(*(tbl + row) + (col))
X
X#define MINROWS 100 	/* minimum size at startup */
X#define MINCOLS 30 
X#define	ABSMAXCOLS 702	/* absolute cols: ZZ (base 26) */
X
X#define RESCOL 4	/* columns reserved for row numbers */
X#define RESROW 3 /* rows reserved for prompt, error, and column numbers */
X
X#define DEFWIDTH 10	/* Default column width and precision */
X#define DEFPREC   2
X#define DEFREFMT  0     /* Make default format fixed point  THA 10/14/90 */
X
X#define HISTLEN  10	/* Number of history entries for vi emulation */
X#ifdef PSC
X# define error(msg)	fprintf(stderr, msg);
X#else
X# define error (void)move(1,0), (void)clrtoeol(), (void) printw
X#endif
X#define	FBUFLEN	1024	/* buffer size for a single field */
X#define	PATHLEN	1024	/* maximum path length */
X
X#ifndef A_CHARTEXT	/* Should be defined in curses.h */
X# ifdef INTERNATIONAL
X#  define A_CHARTEXT 0xff
X# else
X#  define A_CHARTEXT 0x7f
X# endif
X#endif
X
X#if (defined(BSD42) || defined(BSD43)) && !defined(strrchr)
X#define strrchr rindex
X#endif
X
X#if (defined(BSD42) || defined(BSD43)) && !defined(strchr)
X#define strchr index
X#endif
X
X#ifdef SYSV4
Xsize_t	strlen();
X#endif
X
X#ifndef FALSE
X# define	FALSE	0
X# define	TRUE	1
X#endif /* !FALSE */
X
X/*
X * ent_ptr holds the row/col # and address type of a cell
X *
X * vf is the type of cell address, 0 non-fixed, or bitwise OR of FIX_ROW or
X *	FIX_COL
X * vp : we just use vp->row or vp->col, vp may be a new cell just for holding
X *	row/col (say in gram.y) or a pointer to an existing cell
X */
Xstruct ent_ptr {
X    int vf;
X    struct ent *vp;
X};
X
X/* holds the beginning/ending cells of a range */
Xstruct range_s {
X	struct ent_ptr left, right;
X};
X
X/*
X * Some not too obvious things about the flags:
X *    is_valid means there is a valid number in v.
X *    label set means it points to a valid constant string.
X *    is_strexpr set means expr yields a string expression.
X *    If is_strexpr is not set, and expr points to an expression tree, the
X *        expression yields a numeric expression.
X *    So, either v or label can be set to a constant. 
X *        Either (but not both at the same time) can be set from an expression.
X */
X
X#define VALID_CELL(p, r, c) ((p = *ATBL(tbl, r, c)) && \
X			     ((p->flags & is_valid) || p->label))
X
X/* info for each cell, only alloc'd when something is stored in a cell */
Xstruct ent {
X    double v;		/* v && label are set in EvalAll() */
X    char *label;
X    struct enode *expr;	/* cell's contents */
X    short flags;	
X    short row, col;
X    struct ent *next;	/* next deleted ent (pulled, deleted cells) */
X    char *format;	/* printf format for this cell */
X    char cellerror;	/* error in a cell? */
X};
X
X/* stores a range (left, right) */
Xstruct range {
X    struct ent_ptr r_left, r_right;
X    char *r_name;			/* possible name for this range */
X    struct range *r_next, *r_prev;	/* chained ranges */
X    int r_is_range;
X};
X
X#define FIX_ROW 1
X#define FIX_COL 2
X
X/* stores type of operation this cell will preform */
Xstruct enode {
X    int op;
X    union {
X	double k;		/* constant # */
X	struct ent_ptr v;	/* ref. another cell */
X	struct range_s r;	/* op is on a range */
X	char *s;		/* string part of a cell */
X	struct {		/* other cells use to eval()/seval() */
X	    struct enode *left, *right;
X	} o;
X    } e;
X};
X
X/* op values */
X#define O_VAR 'v'
X#define O_CONST 'k'
X#define O_ECONST 'E'	/* constant cell w/ an error */
X#define O_SCONST '$'
X#define REDUCE 0200	/* Or'ed into OP if operand is a range */
X
X#define OP_BASE 256
X#define ACOS OP_BASE + 0
X#define ASIN OP_BASE + 1
X#define ATAN OP_BASE + 2
X#define CEIL OP_BASE + 3
X#define COS OP_BASE + 4
X#define EXP OP_BASE + 5 
X#define FABS OP_BASE + 6 
X#define FLOOR OP_BASE + 7
X#define HYPOT OP_BASE + 8
X#define LOG OP_BASE + 9
X#define LOG10 OP_BASE + 10
X#define POW OP_BASE + 11
X#define SIN OP_BASE + 12
X#define SQRT OP_BASE + 13
X#define TAN OP_BASE + 14
X#define DTR OP_BASE + 15
X#define RTD OP_BASE + 16
X#define MIN OP_BASE + 17
X#define MAX OP_BASE + 18
X#define RND OP_BASE + 19
X#define HOUR OP_BASE + 20
X#define MINUTE OP_BASE + 21
X#define SECOND OP_BASE + 22
X#define MONTH OP_BASE + 23
X#define DAY OP_BASE + 24
X#define YEAR OP_BASE + 25
X#define NOW OP_BASE + 26
X#define DATE OP_BASE + 27
X#define FMT OP_BASE + 28
X#define SUBSTR OP_BASE + 29
X#define STON OP_BASE + 30
X#define EQS OP_BASE + 31
X#define EXT OP_BASE + 32
X#define ELIST OP_BASE + 33	/* List of expressions */
X#define LMAX  OP_BASE + 34
X#define LMIN  OP_BASE + 35
X#define NVAL OP_BASE + 36
X#define SVAL OP_BASE + 37
X#define PV OP_BASE + 38
X#define FV OP_BASE + 39
X#define PMT OP_BASE + 40
X#define STINDEX OP_BASE + 41
X#define LOOKUP OP_BASE + 42
X#define ATAN2 OP_BASE + 43
X#define INDEX OP_BASE + 44
X#define DTS OP_BASE + 45
X#define TTS OP_BASE + 46
X#define ABS OP_BASE + 47 
X#define HLOOKUP OP_BASE + 48
X#define VLOOKUP OP_BASE + 49
X#define ROUND OP_BASE + 50
X#define IF OP_BASE + 51
X#define MYROW OP_BASE + 52
X#define MYCOL OP_BASE + 53
X#define COLTOA OP_BASE + 54
X
X/* flag values */
X#define is_valid     0001
X#define is_changed   0002
X#define is_strexpr   0004
X#define is_leftflush 0010
X#define is_deleted   0020
X
X/* cell error (1st generation (ERROR) or 2nd+ (INVALID)) */
X#define	CELLOK		0
X#define	CELLERROR	1
X#define	CELLINVALID	2
X
X#define ctl(c) ((c)&037)
X#define ESC 033
X#define DEL 0177
X
X/* calculation order */
X#define BYCOLS 1
X#define BYROWS 2
X
X/* tblprint style output for: */
X#define	TBL	1		/* 'tbl' */
X#define	LATEX	2		/* 'LaTeX' */
X#define	TEX	3		/* 'TeX' */
X#define	SLATEX	4		/* 'SLaTeX' (Scandinavian LaTeX) */
X
X/* Types for etype() */
X#define NUM	1
X#define STR	2
X
X#define	GROWAMT	30	/* default minimum amount to grow */
X
X#define	GROWNEW		1	/* first time table */
X#define	GROWROW		2	/* add rows */
X#define	GROWCOL		3	/* add columns */
X#define	GROWBOTH	4	/* grow both */
Xextern	struct ent ***tbl;	/* data table ref. in vmtbl.c and ATBL() */
X
Xextern	char curfile[];
Xextern	int strow, stcol;
Xextern	int currow, curcol;
Xextern	int savedrow, savedcol;
Xextern	int FullUpdate;
Xextern	int maxrow, maxcol;
Xextern	int maxrows, maxcols;	/* # cells currently allocated */
Xextern	int *fwidth;
Xextern	int *precision;
Xextern  int *realfmt;
Xextern	char *col_hidden;
Xextern	char *row_hidden;
Xextern	char line[FBUFLEN];
Xextern	int linelim;
Xextern	int changed;
Xextern	struct ent *to_fix;
Xextern	int showsc, showsr;
X
Xextern	FILE *openout();
Xextern	char *coltoa();
Xextern	char *findhome();
Xextern	char *r_name();
Xextern	char *strrchr();
Xextern	char *v_name();
Xextern	char *xmalloc();
Xextern	char *xrealloc();
Xextern	int are_ranges();
Xextern	int atocol();
Xextern	int cwritefile();
Xextern	int engformat();
Xextern	int etype();
Xextern	int fork();
Xextern	int format();
Xextern	int get_rcqual();
Xextern	int growtbl();
Xextern	int modcheck();
Xextern	int nmgetch();
Xextern	int writefile();
Xextern	int yn_ask();
Xextern	struct enode *copye();
Xextern	struct enode *new();
Xextern	struct enode *new_const();
Xextern	struct enode *new_range();
Xextern	struct enode *new_str();
Xextern	struct enode *new_var();
Xextern	struct ent *lookat();
Xextern	struct range *find_range();
Xextern	void EvalAll();
Xextern	void add_range();
Xextern	void backcol();
Xextern	void backrow();
Xextern	void checkbounds();
Xextern	void clearent();
Xextern	void clean_range();
Xextern	void closecol();
Xextern	void closeout();
Xextern	void closerow();
Xextern	void colshow_op();
Xextern	void copy();
Xextern	void copyent();
Xextern	void creadfile();
Xextern	void deleterow();
Xextern	void del_range();
Xextern	void deraw();
Xextern	void diesave();
Xextern	void doend();
Xextern	void doformat();
Xextern	void dupcol();
Xextern	void duprow();
Xextern	void editexp();
Xextern	void editfmt();
Xextern	void edit_mode();
Xextern	void edits();
Xextern	void editv();
Xextern	void efree();
Xextern	void erase_area();
Xextern	void erasedb();
Xextern	void eraser();
Xextern	void fill();
Xextern	void flush_saved();
Xextern	void format_cell();
Xextern	void forwcol();
Xextern	void forwrow();
Xextern	void free_ent();
Xextern	void go_last();
Xextern	void goraw();
Xextern	void help();
Xextern	void hide_col();
Xextern	void hide_row();
Xextern	void hidecol();
Xextern	void hiderow();
Xextern	void initkbd();
Xextern	void ins_string();
Xextern	void insert_mode();
Xextern	void insertrow();
Xextern	void kbd_again();
Xextern	void label();
Xextern	void let();
Xextern	void list_range();
Xextern	void moveto();
Xextern	void num_search();
Xextern	void opencol();
Xextern	void printfile();
Xextern	void pullcells();
Xextern	void readfile();
Xextern	void resetkbd();
Xextern	void rowshow_op();
Xextern	void setauto();
Xextern	void setiterations();
Xextern	void setorder();
Xextern	void showcol();
Xextern	void showdr();
Xextern	void showrow();
Xextern	void showstring();
Xextern	void signals();
Xextern	void slet();
Xextern	void startshow();
Xextern	void str_search();
Xextern	void sync_ranges();
Xextern	void sync_refs();
Xextern	void tblprintfile();
Xextern	void valueize_area();
Xextern	void write_fd();
Xextern	void write_line();
Xextern	void write_range();
Xextern	void xfree();
Xextern	void yyerror();
X#ifdef DOBACKUPS
Xextern	int backup_file();
X#endif
X
Xextern	int modflg;
Xextern	int Crypt;
Xextern	char *mdir;
Xextern	double prescale;
Xextern	int extfunc;
Xextern	int propagation;
Xextern	int calc_order;
Xextern	int autocalc;
Xextern	int autolabel;
Xextern	int numeric;
Xextern	int showcell;
Xextern	int showtop;
Xextern	int loading;
Xextern	int getrcqual;
Xextern	int tbl_style;
Xextern	int rndinfinity;
Xextern	char *progname;
X
X#if BSD42 || SYSIII
X
X#ifndef cbreak
X#define	cbreak		crmode
X#define	nocbreak	nocrmode
X#endif
X
X#endif
X
X#if defined(BSD42) || defined(BSD43) && !defined(ultrix)
X#define	memcpy(dest, source, len)	bcopy(source, dest, (unsigned int)len);
X#define	memset(dest, zero, len)		bzero((dest), (unsigned int)(len));
X#endif
END_OF_FILE
  if test 9929 -ne `wc -c <'sc.h'`; then
    echo shar: \"'sc.h'\" unpacked with wrong size!
  fi
  # end of 'sc.h'
fi
echo shar: End of archive 1 \(of 7\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 7 archives.
    rm -f ark[1-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.