[comp.sources.bugs] Sc6.8->Sc6.9 patches

buhrt@sawmill.uucp (Jeffery A Buhrt) (10/31/90)

Rich Salz posted these for me almost a month ago to comp.source.bugs
when we didn't have outbound news.

						-Jeff Buhrt
						317-477-6000
					sequent!sawmill!buhrt

*** posted/CHANGES	Mon Sep 24 09:33:48 1990
--- CHANGES	Mon Sep 24 09:44:14 1990
***************
*** 1,3
  CHANGES BETWEEN 6.1 and 6.7
  
  Dave Lewis - 

--- 1,114 -----
+ 
+ CHANGES BETWEEN 6.9 and 6.8
+ 
+ Jim Richardson
+ 	- pointed out vi mode was not documented in sc.doc
+ 	- found a nasty buffer limit bug in savedot()
+ 	- a side effect was ^D could cause a core dump (-Jeff)
+ Tim Wilson
+ 	- Hints on compiling on Ultrix
+ Eric Putz
+ 	-patch for printfile() (sc died on huge # of columns in a W)
+ Jeffrey C Honig
+ 	-patch for lex.c which bombed on SunOS 4.1 if $TERM was not set
+ Tom Kloos
+ 	-psc now calls [+-.] strings vs numbers.
+ 	-also pointed out a format reversal problem
+ Jack Goral
+ 	-changes to Makefile to compile under SCO Unix V rel 3.2.0
+ Mark Nagel
+ 	-changes to allow arbitrarily complex formatting of cells 
+ Kim Sanders
+ 	-^W generated an incorrect equation (line was not started at beginning)
+ Mike Schwartz
+ 	-a put command will use the same encryption key as when the
+ 	file was read.
+ 	>I have a suggestion for making the encyrption option of "sc" more
+ 	>usable:  Right now, if you use the -x option when you start up sc, it
+ 	>prompts you for the key (just like "vi -x" does).  But when you try to
+ 	>write the file out using the Put command, it asks for the key again
+ 	>each time.  Why not make it use the same key you used before (as "vi
+ 	>-x" does)?  That would really help, because as it is, each time you try
+ 	>to save the file you run the risk of mistyping the key.
+ 	>
+ 	>You might think this causes a security problem, since the key is then
+ 	>an argument to crypt, and hence is visible from ps.  But when crypt
+ 	>runs, the first thing it does is to copy the key to an internal buffer
+ 	>and then zero out the argv copy, so the window of vulnerability is
+ 	>vanishingly small.
+ Adri Verhoef
+ 	- pointed out a ^D caused a core dump (fixed)
+ Gene H. Olson
+ 	- format now grows the spreadsheet before setting the column format.
+ 	- removed an extra ';' that caused a possible column number trashing
+ Paul Eggert
+ 	-sc now also has round-to-even, also known as ``banker's rounding''.
+ 	>With round-to-even, a number exactly halfway between two values is
+ 	>rounded to whichever is even; e.g. rnd(0.5)=0, rnd(1.5)=2,
+ 	>rnd(2.5)=2, rnd(3.5)=4.  This is the default rounding mode for
+ 	>IEEE floating point, for good reason: it has better numeric
+ 	>properties.  For example, if X+Y is an integer,
+ 	>then X+Y = rnd(X)+rnd(Y) with round-to-even,
+ 	>but not always with sc's rounding (which is
+ 	>round-to-positive-infinity).  I ran into this problem when trying to
+ 	>split interest in an account to two people fairly.
+ 	-While we're on the subject, @round(X,Y) should also work when Y
+ 	>is negative. For example, @round(123,-2) should yield 100.
+ 
+ 
+ CHANGES BETWEEN 6.8 and 6.7
+ 
+ Jeff Buhrt (with help from some beta testers-Thank you)
+ 	  1) added a per row memory allocation
+ 		-runs in about 1/2 run time and 1/3 the space of 6.6vm.1
+ 		-insert/delete row now just moves pointers (# == maxrow+1-currow)
+ 			and blanks one row (of columns (maxcol))
+ 		-as the number of cells grows the size is more linear
+ 			(no more ##Meg images except for 100,000's of rows....)
+ 		-row to column pointer translation is done by a macro (ATBL)
+ 			that returns a pointer to the cell pointer.
+ 			*ATBL would be a pointer to a *ent (cell).
+ 		-the maximum # of columns is limited by ABSMAXCOLS or
+ 			sizeof(struct ent *)*maxcols (whichever is smaller)
+ 			(702 * 4 = 2808 is no real limit even for 286 large model)
+ 		-the maximum # of rows is limited by the virtual memory limit or
+ 			sizeof(struct ent **)*maxrows (whichever is smaller)
+ 			(4*X=64k, X=16384 rows (excluding malloc overhead) on
+ 				a '286 large model. Even w/ 3.25Meg and 10Mhz)
+ 			(plus of course any memory used for cells)
+ 	2) dolookup (int vs double)
+ 	3) dolookup calling eval w/ ent * not enode *
+ 		 (dolookup called w/ ent * not enode *)
+ 	4) cleaned up a lot of .... *x = 0 to  (.... *)0 (cmds, interp)
+ 	5) psc: fwidth/precision were reversed on the output
+ 	6) Backup copy (on save) using same mode to [path/]#file~
+ 		 (will prompt if a backup fails)
+ 	7) put y/n prompt function into yn_ask(mesg)
+ 	8) found a move(x,y) in sc -> move(y,x) and only move when needed
+ 	9) we use FullUpdate || changed (to see if ANY cells changed) 
+ 		before trying to redraw the screen in update
+ 		(now we don't try to redraw every time a key is hit)
+ 		-if we are stand[ing]out we do not create a cell just to force a
+ 		 standout inside the repaint section of update()
+ 		-only draw blank cells if we cleared it or it is standing out
+ 		reason: the less work (what to update) curses has to do, the faster
+ 			a screen update will be (less cpu required)
+ 	14) {insert, delete}col replaced w/ {open,close}col(currow, numcol_to_insert)
+ 		(limits looping)
+ 	6.7.1.1
+ 	15) goto nonexistant cell may loop
+ 	16) make sure that startup size will at least fill the screen w/ cells.
+ 	17) added version.c
+ 	6.7.1.2
+ 	18) When we would normally die w/o saving (SIGQUIT, etc), we now ask
+ 		if people would like to save the current spreadsheet.
+ 		If 'y', saves to the current file name, otherwise ~/SC.SAVE,
+ 		then /tmp/SC.SAVE if all else fails.
+ 	6.7.1.3
+ 	19) don't use malloc.c for production code
+ 	20) progname is now truncated to just the basename (systems w/ long paths
+ 		caused problems)
+ 
  CHANGES BETWEEN 6.1 and 6.7
  
  Dave Lewis - 
*** posted/Makefile	Mon Sep 24 09:33:59 1990
--- Makefile	Mon Sep 24 09:48:25 1990
***************
*** 5,11
  NAME=SC
  
  # This is where the install step puts it.
! #EXDIR=/v/rgb/bin/psx
  EXDIR=/usr/local/bin
  
  # This is where the man page goes.

--- 5,11 -----
  NAME=SC
  
  # This is where the install step puts it.
! #EXDIR=/site/bin
  EXDIR=/usr/local/bin
  
  # This is where the man page goes.
***************
*** 9,15
  EXDIR=/usr/local/bin
  
  # This is where the man page goes.
! #MANDIR=/usr/man/man1
  MANDIR=/usr/man/manl
  
  # Set SIMPLE for lex.c if you don't want arrow keys or lex.c blows up

--- 9,15 -----
  EXDIR=/usr/local/bin
  
  # This is where the man page goes.
! #MANDIR=/site/man/man1
  MANDIR=/usr/man/manl
  
  # This is where the library file (tutorial) goes.
***************
*** 12,17
  #MANDIR=/usr/man/man1
  MANDIR=/usr/man/manl
  
  # Set SIMPLE for lex.c if you don't want arrow keys or lex.c blows up
  #SIMPLE=-DSIMPLE
  

--- 12,21 -----
  #MANDIR=/site/man/man1
  MANDIR=/usr/man/manl
  
+ # This is where the library file (tutorial) goes.
+ #LIBDIR=/site/lib/sc
+ LIBDIR=/usr/local/lib/sc
+ 
  # Set SIMPLE for lex.c if you don't want arrow keys or lex.c blows up
  #SIMPLE=-DSIMPLE
  
***************
*** 29,34
  #
  #IEEE_MATH=-DIEEE_MATH
  
  # This is the name of a pager like "more" If the line is commented out
  # then "more" will be used. "pg" may be appropriate for SYSV
  PAGER=-DDFLT_PAGER=\"less\"

--- 33,41 -----
  #
  #IEEE_MATH=-DIEEE_MATH
  
+ # Set RINT if you do not have rint() in math.h
+ RINT=-DRINT
+ 
  # This is the name of a pager like "more" If the line is commented out
  # then "more" will be used. "pg" may be appropriate for SYSV
  PAGER=-DDFLT_PAGER=\"less\"
***************
*** 33,38
  # then "more" will be used. "pg" may be appropriate for SYSV
  PAGER=-DDFLT_PAGER=\"less\"
  
  # Use this for system V.2
  #CFLAGS= -O -DSYSV2 
  #LDFLAGS=

--- 40,48 -----
  # then "more" will be used. "pg" may be appropriate for SYSV
  PAGER=-DDFLT_PAGER=\"less\"
  
+ # For ULTRIX: define the BSD4.2 section and SIGVOID above
+ #	tdw@cl.cam.ac.uk tested on Ultrix 3.1C-0
+ 
  # Use this for system V.2
  #CFLAGS= -O -DSYSV2 
  #LDFLAGS=
***************
*** 56,62
  # Use this for Sequent boxes
  CC=atscc
  CFLAGS=-O -DBSD42
! LDFLAGS= -s
  LIB=-lm -lcurses -ltermcap
  PSCLIB=-lseq
  

--- 66,72 -----
  # Use this for Sequent boxes
  CC=atscc
  CFLAGS=-O -DBSD42
! #LDFLAGS= -s
  LIB=-lm -lcurses -ltermcap
  PSCLIB=-lseq
  
***************
*** 75,80
  #LDFLAGS= -z -i 
  #LIB=-lm -lcurses -ltermcap
  
  # All of the source files
  SRC=sc.h sc.c lex.c gram.y interp.c crypt.c xmalloc.c cmds.c range.c help.c \
  	vi.c eres.sed sres.sed Makefile psc.c vmtbl.c version.c

--- 85,101 -----
  #LDFLAGS= -z -i 
  #LIB=-lm -lcurses -ltermcap
  
+ # For SCO Unix V rel. 3.2.0
+ #       -compile using rcc, cc does not cope with gram.c
+ #       -edit /usr/include/curses.h, rcc does not understand #error
+ #       -link: make CC=cc, rcc's loader gets unresolved __cclass, __range
+ #               (rather strange,?)
+ #CC=rcc
+ #SIGVOID=-DSIGVOID
+ #CFLAGS= -O -DSYSV3
+ #LDFLAGS=
+ #LIB=-lm -lcurses -ltinfo -lPW
+ 
  # All of the source files
  SRC=sc.h sc.c lex.c gram.y interp.c crypt.c xmalloc.c cmds.c range.c help.c \
  	vi.c eres.sed sres.sed Makefile psc.c vmtbl.c version.c
***************
*** 81,87
  
  # The objects
  OBJS=sc.o interp.o cmds.o crypt.o xmalloc.o range.o help.o vi.o lex.o gram.o \
! 	vmtbl.o version.o
  
  # The documents in the Archive
  DOCS=README CHANGES sc.doc psc.doc tutorial.sc VMS_NOTES BSD_BUGS

--- 102,108 -----
  
  # The objects
  OBJS=sc.o interp.o cmds.o crypt.o xmalloc.o range.o help.o vi.o lex.o gram.o \
! 	vmtbl.o format.o version.o
  
  # The documents in the Archive
  DOCS=README CHANGES sc.doc psc.doc tutorial.sc VMS_NOTES BSD_BUGS
***************
*** 107,113
  	$(CC) ${CFLAGS} ${INTERNATIONAL} ${PAGER} ${SIGVOID} -c sc.c
  
  interp.o:	interp.c sc.h
! 	$(CC) ${CFLAGS} ${IEEE_MATH} ${SIGVOID} -c interp.c
  
  gram.o:	sc.h y.tab.h
  

--- 128,134 -----
  	$(CC) ${CFLAGS} ${INTERNATIONAL} ${PAGER} ${SIGVOID} -c sc.c
  
  interp.o:	interp.c sc.h
! 	$(CC) ${CFLAGS} ${IEEE_MATH} ${SIGVOID} ${RINT} -c interp.c
  
  gram.o:	sc.h y.tab.h
  
***************
*** 121,126
  
  help.o: help.c sc.h
  
  vi.o: vi.c sc.h
  
  y.tab.h:	gram.y

--- 142,149 -----
  
  help.o: help.c sc.h
  
+ format.o: format.c
+ 
  vi.o: vi.c sc.h
  
  y.tab.h:	gram.y
***************
*** 154,160
  	prc sc.h gram.y sc.c lex.c interp.c cmds.c crypt.c | lpr
  
  $(name).1:	sc.doc
! 	sed -e s/pname/$(name)/g -e s/PNAME/$(NAME)/g sc.doc >  $(name).1
  
  $(name).man:	$(name).1
  	nroff -man $(name).1 > $(name).man

--- 177,184 -----
  	prc sc.h gram.y sc.c lex.c interp.c cmds.c crypt.c | lpr
  
  $(name).1:	sc.doc
! 	sed -e s/pname/$(name)/g -e s/PNAME/$(NAME)/g \
! 	   -e s%#LIBDIR#%$(LIBDIR)%g sc.doc >  $(name).1
  
  $(name).man:	$(name).1
  	nroff -man $(name).1 > $(name).man
***************
*** 168,174
  p$(name).man:	p$(name).1
  	nroff -man p$(name).1 > p$(name).man
  
! install: $(EXDIR)/$(name)
  
  inst-man: $(MANDIR)/$(name).1
  

--- 192,198 -----
  p$(name).man:	p$(name).1
  	nroff -man p$(name).1 > p$(name).man
  
! install: $(EXDIR)/$(name) $(LIBDIR)/tutorial
  
  inst-man: $(MANDIR)/$(name).1
  
***************
*** 176,181
  	-mv $(EXDIR)/$(name) $(EXDIR)/$(name).old
  	cp $(name) $(EXDIR)
  	strip $(EXDIR)/$(name)
  
  $(MANDIR)/$(name).1: $(name).1
  	cp $(name).1 $(MANDIR)

--- 200,209 -----
  	-mv $(EXDIR)/$(name) $(EXDIR)/$(name).old
  	cp $(name) $(EXDIR)
  	strip $(EXDIR)/$(name)
+ 
+ $(LIBDIR)/tutorial: tutorial.sc
+ 	-mkdir $(LIBDIR)
+ 	cp tutorial.sc $(LIBDIR)
  
  $(MANDIR)/$(name).1: $(name).1
  	cp $(name).1 $(MANDIR)
*** posted/TODO	Mon Sep 24 09:34:09 1990
--- TODO	Wed Sep  5 15:19:17 1990
***************
*** 1,54
- done/tested: (Jeff Buhrt)
- 6.7
- 1) added a per row memory allocation
- 	-runs in about 1/2 run time and 1/3 the space of 6.6vm.1
- 	-insert/delete row now just moves pointers (# == maxrow+1-currow)
- 		and blanks one row (of columns (maxcol))
- 	-as the number of cells grows the size is more linear
- 		(no more ##Meg images except for 100,000's of rows....)
- 	-row to column pointer translation is done by a macro (ATBL)
- 		that returns a pointer to the cell pointer.
- 		*ATBL would be a pointer to a *ent (cell).
- 	-the maximum # of columns is limited by ABSMAXCOLS or
- 		sizeof(struct ent *)*maxcols (whichever is smaller)
- 		(702 * 4 = 2808 is no real limit even for 286 large model)
- 	-the maximum # of rows is limited by the virtual memory limit or
- 		sizeof(struct ent **)*maxrows (whichever is smaller)
- 		(4*X=64k, X=16384 rows (excluding malloc overhead) on
- 			a '286 large model. Even w/ 3.25Meg and 10Mhz)
- 		(plus of course any memory used for cells)
- 2) dolookup (int vs double)
- 3) dolookup calling eval w/ ent * not enode *
- 	 (dolookup called w/ ent * not enode *)
- 4) cleaned up a lot of .... *x = 0 to  (.... *)0 (cmds, interp)
- 5) psc: fwidth/precision were reversed on the output
- 6) Backup copy (on save) using same mode to [path/]#file~
- 	 (will prompt if a backup fails)
- 7) put y/n prompt function into yn_ask(mesg)
- 8) found a move(x,y) in sc -> move(y,x) and only move when needed
- 9) we use FullUpdate || changed (to see if ANY cells changed) 
- 	before trying to redraw the screen in update
- 	(now we don't try to redraw every time a key is hit)
- 	-if we are stand[ing]out we do not create a cell just to force a
- 	 standout inside the repaint section of update()
- 	-only draw blank cells if we cleared it or it is standing out
- 	reason: the less work (what to update) curses has to do, the faster
- 		a screen update will be (less cpu required)
- 14) {insert, delete}col replaced w/ {open,close}col(currow, numcol_to_insert)
- 	(limits looping)
- 6.7.1.1
- 15) goto nonexistant cell may loop
- 16) make sure that startup size will at least fill the screen w/ cells.
- 17) added version.c
- 6.7.1.2
- 18) When we would normally die w/o saving (SIGQUIT, etc), we now ask
- 	if people would like to save the current spreadsheet.
- 	If 'y', saves to the current file name, otherwise ~/SC.SAVE,
- 	then /tmp/SC.SAVE if all else fails.
- 6.7.1.3
- 19) don't use malloc.c for production code
- 20) progname is now truncated to just the basename (systems w/ long paths
- 	caused problems)
  
  todo:
  1) autobackup of things typed in.

--- 1,3 -----
  
  todo:
  1) autobackup of things typed in.
*** posted/cmds.c	Mon Sep 24 09:34:48 1990
--- cmds.c	Mon Sep 24 09:41:56 1990
***************
*** 7,13
   *
   *              More mods Robert Bond, 12/86
   *
!  *		$Revision: 6.8 $
   */
  
  #include <curses.h>

--- 7,13 -----
   *
   *              More mods Robert Bond, 12/86
   *
!  *		$Revision: 6.9 $
   */
  
  #include <curses.h>
***************
*** 583,588
  {
      register int i;
  
      if (w > COLS - RESCOL - 2) {
  	error("Format too large - Maximum = %d", COLS - RESCOL - 2);
  	w = COLS-RESCOL-2;

--- 583,591 -----
  {
      register int i;
  
+     if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ;
+     if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ;
+ 
      if (w > COLS - RESCOL - 2) {
  	error("Format too large - Maximum = %d", COLS - RESCOL - 2);
  	w = COLS-RESCOL-2;
***************
*** 647,653
  int r0, c0, rn, cn;
  {
      FILE *f;
!     char pline[FBUFLEN];
      int plinelim;
      int pid;
      int fieldlen, nextcol;

--- 650,656 -----
  int r0, c0, rn, cn;
  {
      FILE *f;
!     char *pline;
      int plinelim;
      int fbufs_allocated = 0;
      int pid;
***************
*** 649,654
      FILE *f;
      char pline[FBUFLEN];
      int plinelim;
      int pid;
      int fieldlen, nextcol;
      register row, col;

--- 652,658 -----
      FILE *f;
      char *pline;
      int plinelim;
+     int fbufs_allocated = 0;
      int pid;
      int fieldlen, nextcol;
      register row, col;
***************
*** 654,659
      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;

--- 658,668 -----
      register row, col;
      register struct ent **pp;
  
+     if((pline = (char *)malloc(FBUFLEN * ++fbufs_allocated)) == (char *)NULL) {
+         error("Malloc failed in printfile()");
+         return;
+     }
+ 
      if ((strcmp(fname, curfile) == 0) &&
  	!yn_ask("Confirm that you want to destroy the data base: (y,n)")) {
          free((char *)pline);
***************
*** 655,661
      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)

--- 664,671 -----
      }
  
      if ((strcmp(fname, curfile) == 0) &&
! 	!yn_ask("Confirm that you want to destroy the data base: (y,n)")) {
!         free((char *)pline);
  	return;
      }
  
***************
*** 657,662
      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);

--- 667,673 -----
  	!yn_ask("Confirm that you want to destroy the data base: (y,n)")) {
          free((char *)pline);
  	return;
+     }
  
      if ((f = openout(fname, &pid)) == (FILE *)0)
      {	error ("Can't create file \"%s\"", fname);
***************
*** 660,665
  
      if ((f = openout(fname, &pid)) == (FILE *)0)
      {	error ("Can't create file \"%s\"", fname);
  	return;
      }
      for (row=r0;row<=rn; row++) {

--- 671,677 -----
  
      if ((f = openout(fname, &pid)) == (FILE *)0)
      {	error ("Can't create file \"%s\"", fname);
+         free((char *)pline);
  	return;
      }
      for (row=r0;row<=rn; row++) {
***************
*** 682,687
  	    if (*pp) {
  		char *s;
  
  		while (plinelim<c) pline[plinelim++] = ' ';
  		plinelim = c;
  		if ((*pp)->flags&is_valid) {

--- 694,713 -----
  	    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 = ((char *)realloc
+ 			       ((char *)pline, 
+ 				(unsigned)(FBUFLEN * ++fbufs_allocated))))
+ 		     == NULL) {
+ 		    error ("Realloc failed in printfile()");
+ 		    free((char *)pline);
+ 		    return;
+ 		  }
+ 		}		  
  		while (plinelim<c) pline[plinelim++] = ' ';
  		plinelim = c;
  		if ((*pp)->flags&is_valid) {
***************
*** 685,692
  		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) {

--- 711,738 -----
  		while (plinelim<c) pline[plinelim++] = ' ';
  		plinelim = c;
  		if ((*pp)->flags&is_valid) {
! 		    while(plinelim + fwidth[col] > 
! 			  (fbufs_allocated * FBUFLEN)) {
! 		      if((pline = ((char *)realloc
! 				   ((char *)pline, 
! 				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
! 			 == NULL) {
! 			error ("Realloc failed in printfile()");
! 			free((char *)pline);
! 			return;
! 		      }
! 		    }		  
! 		    if ((*pp)->format) {
! 			char field[FBUFLEN];
! 
! 			(void) format ((*pp)->format, (*pp)->v, field,
! 				       sizeof(field));
! 			(void) sprintf (pline+plinelim, "%*s", fwidth[col],
! 					field);
! 		    } else {
! 			(void)sprintf (pline+plinelim,"%*.*f",fwidth[col],
! 				       precision[col], (*pp)->v);
! 		    }
  		    plinelim += strlen (pline+plinelim);
  		}
  		if (s = (*pp)->label) {
***************
*** 709,714
  		    if (slen > fieldlen)
  			slen = fieldlen;
  		    
  		    /* Now justify and print */
  		    start = (*pp)->flags & is_leftflush ? pline + c
  					: pline + c + fieldlen - slen;

--- 755,771 -----
  		    if (slen > fieldlen)
  			slen = fieldlen;
  		    
+ 		    while(c + fieldlen + 2 > (fbufs_allocated * FBUFLEN)) {
+ 		      if((pline = ((char *)realloc
+ 				   ((char *)pline, 
+ 				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
+ 			 == NULL) {
+ 			error ("Realloc failed in printfile()");
+ 			free((char *)pline);
+ 			return;
+ 		      }
+ 		    }		  
+ 
  		    /* Now justify and print */
  		    start = (*pp)->flags & is_leftflush ? pline + c
  					: pline + c + fieldlen - slen;
***************
*** 731,736
  	(void) fputs (pline, f);
      }
  
      closeout(f, pid);
  }
  

--- 788,795 -----
  	(void) fputs (pline, f);
      }
  
+     free((char *)pline);
+ 
      closeout(f, pid);
  }
  
***************
*** 780,787
  	    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);

--- 839,854 -----
  	    if (*pp) {
  		char *s;
  		if ((*pp)->flags&is_valid) {
! 		    if ((*pp)->format) {
! 		        char field[FBUFLEN];
! 			
! 			(void) format ((*pp)->format, (*pp)->v, field,
! 				       sizeof(field));
! 			(void) fprintf (f, "%s", field);
! 		    } else {
! 		        (void) fprintf (f,"%.*f",precision[col],
! 					(*pp)->v);
! 		    }
  		}
  		if (s = (*pp)->label) {
  	            (void) fprintf (f,"%s",s);
***************
*** 1090,1096
      n -> label = (char *)0;
      if (p -> label) {
  	n -> label = (char *)
! 		xmalloc  ((unsigned) (strlen (p -> label) + 1));
  	(void) strcpy (n -> label, p -> label);
      }
  }

--- 1157,1163 -----
      n -> label = (char *)0;
      if (p -> label) {
  	n -> label = (char *)
! 		xmalloc ((unsigned) (strlen (p -> label) + 1));
  	(void) strcpy (n -> label, p -> label);
      }
      n -> format = 0;
***************
*** 1093,1098
  		xmalloc  ((unsigned) (strlen (p -> label) + 1));
  	(void) strcpy (n -> label, p -> label);
      }
  }
  
  void

--- 1160,1171 -----
  		xmalloc ((unsigned) (strlen (p -> label) + 1));
  	(void) strcpy (n -> label, p -> label);
      }
+     n -> format = 0;
+     if (p -> format) {
+         n -> format = (char *)
+ 		xmalloc ((unsigned) (strlen (p -> format) + 1));
+ 	(void) strcpy (n -> format, p -> format);
+     }
  }
  
  void
***************
*** 1137,1142
  		    editv (r, c);
  		    (void) fprintf (f, "%s\n",line);
  		}
  	    }
      }
  }

--- 1210,1219 -----
  		    editv (r, c);
  		    (void) fprintf (f, "%s\n",line);
  		}
+ 		if ((*pp)->format) {
+ 		    editfmt (r, c);
+ 		    (void) fprintf (f, "%s\n",line);
+ 		}
  	    }
      }
      if (rndinfinity)
***************
*** 1139,1144
  		}
  	    }
      }
  }
  
  int

--- 1216,1223 -----
  		}
  	    }
      }
+     if (rndinfinity)
+ 	fprintf(f, "set rndinfinity\n");
  }
  
  int
*** posted/crypt.c	Mon Sep 24 09:34:53 1990
--- crypt.c	Mon Sep 24 09:41:59 1990
***************
*** 2,8
   * Encryption utilites
   * Bradley Williams	
   * {allegra,ihnp4,uiucdcs,ctvax}!convex!williams
!  * $Revision: 6.8 $
   */
  
  #include <stdio.h>

--- 2,8 -----
   * Encryption utilites
   * Bradley Williams	
   * {allegra,ihnp4,uiucdcs,ctvax}!convex!williams
!  * $Revision: 6.9 $
   */
  
  #include <stdio.h>
***************
*** 23,28
  #endif
  
  int         Crypt = 0;
  
  creadfile (save, eraseflg)
  char *save;

--- 23,30 -----
  #endif
  
  int         Crypt = 0;
+ #define MAXKEYWORDSIZE 30
+ char	    KeyWord[MAXKEYWORDSIZE] = NULL;
  
  creadfile (save, eraseflg)
  char *save;
***************
*** 49,54
      }
  
      deraw();
      if ((pid=fork()) == 0)			  /* if child  */
      {
  	(void) close (0);		  /* close stdin */

--- 51,59 -----
      }
  
      deraw();
+     strcpy(KeyWord, getpass("Enter key:"));
+     goraw();
+ 
      if ((pid=fork()) == 0)			  /* if child  */
      {
  	(void) close (0);		  /* close stdin */
***************
*** 57,63
  	(void) dup (fildes);		  /* standard in from file */
  	(void) dup (pipefd[1]);		  /* connect to pipe */
  	(void) fprintf (stderr, " ");
! 	(void) execl ("/bin/sh", "sh", "-c", "crypt", (char *)0);
  	exit (-127);
      }
      else				  /* else parent */

--- 62,69 -----
  	(void) dup (fildes);		  /* standard in from file */
  	(void) dup (pipefd[1]);		  /* connect to pipe */
  	(void) fprintf (stderr, " ");
! 	(void) execl ("/bin/crypt", "crypt", KeyWord, 0);
! 
  	exit (-127);
      }
      else				  /* else parent */
***************
*** 82,88
      (void) fclose (f);
      (void) close (pipefd[0]);
      while (pid != wait(&fildes)) /**/;
-     goraw();
      linelim = -1;
      modflg++;
      if (eraseflg) {

--- 88,93 -----
      (void) fclose (f);
      (void) close (pipefd[0]);
      while (pid != wait(&fildes)) /**/;
      linelim = -1;
      modflg++;
      if (eraseflg) {
***************
*** 134,140
  	return(-1);
      }
  
!     deraw();
      if ((pid=fork()) == 0)			  /* if child  */
      {
  	(void) close (0);			  /* close stdin */

--- 139,150 -----
  	return(-1);
      }
  
!     if (KeyWord[0] == NULL) {
! 	deraw();
! 	strcpy(KeyWord, getpass("Enter key:"));
! 	goraw();
!     }
! 
      if ((pid=fork()) == 0)			  /* if child  */
      {
  	(void) close (0);			  /* close stdin */
***************
*** 143,149
  	(void) dup (pipefd[0]);			  /* connect to pipe input */
  	(void) dup (fildes);			  /* standard out to file */
  	(void) fprintf (stderr, " ");
! 	(void) execl ("/bin/sh", "sh", "-c", "crypt", 0);
  	exit (-127);
      }
      else				  /* else parent */

--- 153,159 -----
  	(void) dup (pipefd[0]);			  /* connect to pipe input */
  	(void) dup (fildes);			  /* standard out to file */
  	(void) fprintf (stderr, " ");
! 	(void) execl ("/bin/crypt", "crypt", KeyWord, 0);
  	exit (-127);
      }
      else				  /* else parent */
***************
*** 168,175
      (void) strcpy(curfile,save);
  
      modflg = 0;
!     error ("File \"%s\" written", curfile);
!     goraw();
      return(0);
  }
  

--- 178,184 -----
      (void) strcpy(curfile,save);
  
      modflg = 0;
!     error ("File \"%s\" written (encrypted).", curfile);
      return(0);
  }
  
*** posted/format.c	Mon Sep 24 09:54:28 1990
--- format.c	Mon Sep 24 09:42:10 1990
***************
*** 0

--- 1,461 -----
+ /*****************************************************************************
+  *
+  * Mark Nagel <nagel@ics.uci.edu>
+  * 20 July 1989
+  *
+  * $Revision: 6.9 $
+  *
+  * bool
+  * format(fmt, num, buf, buflen)
+  *  char *fmt;
+  *  double num;
+  *  char buf[];
+  *  int buflen;
+  *
+  * The format function will produce a string representation of a number
+  * given a _format_ (described below) and a double value.  The result is
+  * written into the passed buffer -- if the resulting string is too
+  * long to fit into the passed buffer, the function returns false.
+  * Otherwise the function returns true.
+  *
+  * The fmt parameter contains the format to use to convert the number.
+  *
+  *  #	Digit placeholder.  If the number has fewer digits on either
+  *      side of the decimal point than  there are '#' characters in
+  *      the format, the extra '#' characters are ignored.  The number
+  *      is rounded to the number of digit placeholders as there are
+  *      to the right of the decimal point.  If there are more digits
+  *      in the number than there are digit placeholders on the left
+  *      side of the decimal point, then those digits are displayed.
+  *
+  *  0	Digit placeholder.  Same as for '#' except that the number
+  *      is padded with zeroes on either side of the decimal point.
+  *      The number of zeroes used in padding is determined by the
+  *      number of digit placeholders after the '0' for digits on
+  *      the left side of the decimal point and by the number of
+  *      digit placeholders before the '0' for digits on the right
+  *      side of the decimal point.
+  *
+  *  .	Decimal point.  Determines how many digits are placed on
+  *      the right and left sides of the decimal point in the number.
+  *      Note that numbers smaller than 1 will begin with a decimal
+  *      point if the left side of the decimal point contains only
+  *      a '#' digit placeholder.  Use a '0' placeholder to get a
+  *      leading zero in decimal formats.
+  *
+  *  %	Percentage.  For each '%' character in the format, the actual
+  *      number gets multiplied by 100 (only for purposes of formatting
+  *      -- the original number is left unmodified) and the '%' character
+  *      is placed in the same position as it is in the format.
+  *
+  *  ,	Thousands separator.  The presence of a ',' in the format
+  *      (multiple commas are treated as one) will cause the number
+  *      to be formatted with a ',' separating each set of three digits
+  *      in the integer part of the number with numbering beginning
+  *      from the right end of the integer.
+  *
+  *  \	Quote.  This character causes the next character to be
+  *      inserted into the formatted string directly with no
+  *      special interpretation.
+  *
+  *  E- E+ e- e+
+  *	Scientific format.  Causes the number to formatted in scientific
+  *	notation.  The case of the 'E' or 'e' given is preserved.  If
+  *      the format uses a '+', then the sign is always given for the
+  *	exponent value.  If the format uses a '-', then the sign is
+  *	only given when the exponent value is negative.  Note that if
+  *	there is no digit placeholder following the '+' or '-', then
+  *	that part of the formatted number is left out.  In general,
+  *	there should be one or more digit placeholders after the '+'
+  *	or '-'.
+  *
+  *  ;	Format selector.  Use this character to separate the format
+  *	into two distinct formats.  The format to the left of the
+  *	';' character will be used if the number given is zero or
+  *	positive.  The format to the right of the ';' character is
+  *      used if the number given is negative.
+  *    
+  *  Any
+  *	Self insert.  Any other character will be inserted directly
+  *	into the formatted number with no change made to the actual
+  *      number.
+  *
+  *****************************************************************************/
+ 
+ /*****************************************************************************/
+ 
+ #include <stdio.h>
+ 
+ #define bool	int
+ #define true	1
+ #define false	0
+ #define EOS	'\0'
+ #define MAXBUF	256
+ 
+ extern char
+   *strcpy();
+ 
+ static char
+   *fmt_int(),
+   *fmt_frac(),
+   *fmt_exp();
+ 
+ static void
+   reverse();
+ 
+ /*****************************************************************************/
+ 
+ bool
+ format(fmt, val, buf, buflen)
+   char *fmt;
+   double val;
+   char *buf;
+   int buflen;
+ {
+   register char *cp;
+   char *tmp, *tp, *tmpfmt;
+   bool comma = false, negative = false;
+   char *integer = NULL, *decimal = NULL;
+   char *exponent = NULL;
+   int exp_val, width;
+   char prtfmt[32];
+   char *mantissa;
+   char *fraction = NULL;
+   int zero_pad = 0;
+ 
+   if (fmt == NULL)
+     return;
+ 
+   fmt = tmpfmt = strcpy((char *) xmalloc(strlen(fmt) + 1), fmt);
+   mantissa = (char *) xmalloc(buflen + 1);
+ 
+   /*
+    * select positive or negative format if necessary
+    */
+   for (cp = fmt; *cp != ';' && *cp != EOS; cp++)
+   {
+     if (*cp == '\\')
+       cp++;
+   }
+   if (*cp == ';')
+   {
+     if (val < 0.0)
+     {
+       val = -val;     /* format should provide sign if desired */
+       fmt = cp + 1;
+     }
+     else
+     {
+       *cp = EOS;
+     }
+   }
+   
+   /*
+    * extract other information from format and produce new
+    * malloc'ed format string
+    */
+   tmp = (char *) xmalloc(strlen(fmt) + 1);
+   for (cp = fmt, tp = tmp; *cp != EOS; cp++)
+   {
+     switch (*cp)
+     {
+       case '\\':
+         *tp++ = *cp++;
+         *tp++ = *cp;
+ 	break;
+ 
+       case ',':
+         comma = true;
+ 	break;
+ 
+       case '.':
+         if (decimal == NULL)
+ 	  decimal = tp;
+ 	*tp++ = *cp;
+ 	break;
+ 	
+       case '%':
+         val *= 100.0;
+ 	*tp++ = *cp;
+ 	break;
+ 	
+       default:
+         *tp++ = *cp;
+ 	break;
+     }
+   }
+   *tp = EOS;
+   xfree(tmpfmt);
+   fmt = tmp;
+   
+   /*
+    * extract the exponent from the format if present
+    */
+   for (cp = fmt; *cp != EOS; cp++)
+   {
+     if (*cp == '\\')
+     {
+       cp++;
+     }
+     else if (*cp == 'e' || *cp == 'E')
+     {
+       if (cp[1] == '+' || cp[1] == '-')
+       {
+         tmp = (char *) xmalloc(strlen(cp) + 1);
+ 	exponent = strcpy(tmp, cp);
+ 	*cp = EOS;
+ 	exp_val = 0;
+ 	while (val < 1.0)
+ 	{
+           val *= 10.0;
+ 	  exp_val--;
+ 	}
+ 	while (val >= 10.0)
+ 	{
+ 	  val /= 10.0;
+ 	  exp_val++;
+ 	}
+ 	break;
+       }
+     }
+   }
+ 
+   /*
+    * determine maximum decimal places and use sprintf
+    * to build initial character form of formatted value.
+    */
+   width = 0;
+   if (decimal)
+   {
+     *decimal++ = EOS;
+     for (cp = decimal; *cp != EOS; cp++)
+     {
+       switch (*cp)
+       {
+         case '\\':
+           cp++;
+ 	  break;
+ 
+         case '#':
+           width++;
+ 	  break;
+ 
+ 	case '0':
+ 	  zero_pad = ++width;
+ 	  break;
+       }
+     }
+     zero_pad = strlen(decimal) - zero_pad;
+   }
+   if (val < 0.0)
+   {
+     negative = true;
+     val = -val;
+   }
+   sprintf(prtfmt, "%%.%dlf", width);
+   sprintf(mantissa, prtfmt, val);
+   for (cp = integer = mantissa; *cp != '.' && *cp != EOS; cp++)
+   {
+     if (*integer == '0')
+       integer++;
+   }
+   if (*cp == '.')
+   {
+     fraction = cp + 1;
+     *cp = EOS;
+     cp = fraction + strlen(fraction) - 1;
+     for (; zero_pad > 0; zero_pad--, cp--)
+     {
+       if (*cp == '0')
+         *cp = EOS;
+     }
+   }
+ 
+   /*
+    * format the puppy
+    */
+   {
+     char *ci, *cf, *ce;
+     int len_ci, len_cf, len_ce;
+     bool ret = false;
+     
+     ci = fmt_int(integer, fmt, comma, negative);
+     ci = strcpy((char *)xmalloc((len_ci = strlen(ci)) + 1), ci);
+     cf = (fraction) ? fmt_frac(fraction, decimal) : "";
+     cf = strcpy((char *)xmalloc((len_cf = strlen(cf)) + 1), cf);
+     ce = (exponent) ? fmt_exp(exp_val, exponent) : "";
+     ce = strcpy((char *)xmalloc((len_ce = strlen(ce)) + 1), ce);
+     if (len_ci + len_cf + len_ce < buflen)
+     {
+       sprintf(buf, "%s%s%s", ci, cf, ce);
+       ret = true;
+     }
+ 
+     /*
+      * free up malloc'ed memory
+      */
+     xfree(mantissa);
+     xfree(fmt);
+     if (exponent) xfree(exponent);
+     xfree(ci);
+     xfree(cf);
+     xfree(ce);
+ 
+     return (ret);
+   }
+ }
+ 
+ /*****************************************************************************/
+ 
+ static char *
+ fmt_int(val, fmt, comma, negative)
+   char *val;	    /* integer part of the value to be formatted */
+   char *fmt;	    /* integer part of the format */
+   bool comma;	    /* true if we should comma-ify the value */
+   bool negative;    /* true if the value is actually negative */
+ {
+   int digit, f, v;
+   int thousands = 0;
+   char *cp;
+   static char buf[MAXBUF];
+   char *bufptr = buf;
+ 
+   /*
+    * locate the leftmost digit placeholder
+    */
+   for (cp = fmt; *cp != EOS; cp++)
+   {
+     if (*cp == '\\')
+       cp++;
+     else if (*cp == '#' || *cp == '0')
+       break;
+   }
+   digit = (*cp == EOS) ? -1 : cp - fmt;
+ 
+   /*
+    * format the value
+    */
+   f = strlen(fmt) - 1;
+   v = (digit >= 0) ? strlen(val) - 1 : -1;
+   while (f >= 0 || v >= 0)
+   {
+     if (f > 0 && fmt[f-1] == '\\')
+     {
+       *bufptr++ = fmt[f--];
+     }
+     else if (f >= 0 && (fmt[f] == '#' || fmt[f] == '0'))
+     {
+       if (v >= 0 || fmt[f] == '0')
+       {
+         *bufptr++ = v < 0 ? '0' : val[v];
+ 	if (comma && (thousands = (thousands + 1) % 3) == 0 && v > 0)
+ 	{
+ 	  *bufptr++ = ',';
+ 	}
+ 	v--;
+       }
+     }
+     else if (f >= 0)
+     {
+       *bufptr++ = fmt[f];
+     }
+     if (v >= 0 && f == digit)
+     {
+       continue;
+     }
+     f--;
+   }
+     
+   if (negative && digit >= 0)
+     *bufptr++ = '-';
+   *bufptr = EOS;
+   reverse(buf);
+ 
+   return (buf);
+ }
+ 
+ /*****************************************************************************/
+ 
+ static char *
+ fmt_frac(val, fmt)
+   char *val;	    /* fractional part of the value to be formatted */
+   char *fmt;	    /* fractional portion of format */
+ {
+   static char buf[MAXBUF];
+   register char *bufptr = buf;
+   register char *fmtptr = fmt, *valptr = val;
+ 
+   *bufptr++ = '.';
+   while (*fmtptr != EOS)
+   {
+     if (*fmtptr == '\\')
+     {
+       *bufptr++ = *++fmtptr;
+     }
+     else if (*fmtptr == '#' || *fmtptr == '0')
+     {
+       if (*valptr != EOS || *fmtptr == '0')
+       {
+         *bufptr++ = (*valptr != EOS) ? *valptr++ : *fmtptr;
+       }
+     }
+     else
+     {
+       *bufptr++ = *fmtptr;
+     }
+     fmtptr++;
+   }
+   *bufptr = EOS;
+ 
+   return (buf);
+ }
+ 
+ /*****************************************************************************/
+ 
+ static char *
+ fmt_exp(val, fmt)
+   int val;	    /* value of the exponent */
+   char *fmt;	    /* exponent part of the format */
+ {
+   static char buf[MAXBUF];
+   register char *bufptr = buf;
+   char valbuf[64];
+   bool negative = false;
+   
+   *bufptr++ = *fmt++;
+   if (*fmt++ == '+')
+     *bufptr++ = (val < 0) ? '-' : '+';
+   else if (val < 0)
+     *bufptr++ = '-';
+   *bufptr = EOS;
+ 
+   if (val < 0)
+   {
+     val = -val;
+     negative = true;
+   }
+   sprintf(valbuf, "%d", val);
+   
+   strcat(buf, fmt_int(valbuf, fmt, false, negative));
+   return (buf);
+ }
+ 
+ /*****************************************************************************/
+ 
+ static void
+ reverse(buf)
+   register char *buf;
+ {
+   register char *cp = buf + strlen(buf) - 1;
+   register char tmp;
+ 
+   while (buf < cp)
+   {
+     tmp = *cp;
+     *cp-- = *buf;
+     *buf++ = tmp;
+   }
+ }
+ 
+ /*****************************************************************************/
+   
*** posted/gram.y	Mon Sep 24 09:35:10 1990
--- gram.y	Mon Sep 24 09:42:13 1990
***************
*** 9,15
   *
   *		More mods by Alan Silverstein, 3/88, see list of changes.
   *
!  *		$Revision: 6.8 $
   */
  
  

--- 9,15 -----
   *
   *		More mods by Alan Silverstein, 3/88, see list of changes.
   *
!  *		$Revision: 6.9 $
   */
  
  
***************
*** 46,51
  %token <sval> WORD
  %token <ival> COL
  %token S_FORMAT
  %token S_LABEL
  %token S_LEFTSTRING
  %token S_RIGHTSTRING

--- 46,52 -----
  %token <sval> WORD
  %token <ival> COL
  %token S_FORMAT
+ %token S_FMT
  %token S_LABEL
  %token S_LEFTSTRING
  %token S_RIGHTSTRING
***************
*** 140,145
  %token K_TBL
  %token K_LATEX
  %token K_TEX
  
  %left '?' ':'
  %left '|'

--- 141,147 -----
  %token K_TBL
  %token K_LATEX
  %token K_TEX
+ %token K_RNDINFINITY
  
  %left '?' ':'
  %left '|'
***************
*** 223,228
  				      lookat(currow, curcol), $2, $3); }
  	|	S_FILL var_or_range num num
  				 { fill($2.left.vp, $2.right.vp, $3, $4); }
  	|	S_GOTO var_or_range {moveto($2.left.vp->row, $2.left.vp->col);}
  	|       S_GOTO num          {num_search($2);}
  	|       S_GOTO STRING       {str_search($2);}

--- 225,232 -----
  				      lookat(currow, curcol), $2, $3); }
  	|	S_FILL var_or_range num num
  				 { fill($2.left.vp, $2.right.vp, $3, $4); }
+ 	|	S_FMT var_or_range STRING
+ 				{ format_cell($2.left.vp, $2.right.vp, $3); }
  	|	S_GOTO var_or_range {moveto($2.left.vp->row, $2.left.vp->col);}
  	|       S_GOTO num          {num_search($2);}
  	|       S_GOTO STRING       {str_search($2);}
***************
*** 418,421
  	|	K_TBLSTYLE '=' K_TBL	{ tbl_style = TBL; }
  	|	K_TBLSTYLE '=' K_LATEX	{ tbl_style = LATEX; }
  	|	K_TBLSTYLE '=' K_TEX	{ tbl_style = TEX; }
    	;

--- 422,427 -----
  	|	K_TBLSTYLE '=' K_TBL	{ tbl_style = TBL; }
  	|	K_TBLSTYLE '=' K_LATEX	{ tbl_style = LATEX; }
  	|	K_TBLSTYLE '=' K_TEX	{ tbl_style = TEX; }
+ 	|	K_RNDINFINITY		{ rndinfinity = 1; FullUpdate++;}
+ 	|	'!' K_RNDINFINITY	{ rndinfinity = 0; FullUpdate++;}
    	;
*** posted/help.c	Mon Sep 24 09:35:31 1990
--- help.c	Mon Sep 24 09:42:17 1990
***************
*** 1,7
  /*
   * Help functions for sc 
   * R. Bond, 1988
!  * $Revision: 6.8 $
   */
  
  #include <curses.h>

--- 1,7 -----
  /*
   * Help functions for sc 
   * R. Bond, 1988
!  * $Revision: 6.9 $
   */
  
  #include <curses.h>
***************
*** 53,58
  "          iterations=n  Set the number of iterations allowed. (10)",
  "          tblstyle=xx   Set ``T'' output style to:",
  "                        0 (none), tex, latex, or tbl.",
  (char *)0
  };
  

--- 53,59 -----
  "          iterations=n  Set the number of iterations allowed. (10)",
  "          tblstyle=xx   Set ``T'' output style to:",
  "                        0 (none), tex, latex, or tbl.",
+ "	   rndinfinity   Round to infinity (round .5 up vs to nearest even).",
  (char *)0
  };
  
***************
*** 91,96
  "     \",>  Enter a right justified string or string expression.",
  "     e    Edit the current cell's numeric value.",
  "     E    Edit the current cell's string part.",
  "     x    Clear the current cell.",
  "     c    Copy the last marked cell to the current cell.",
  "     m    Mark a cell to be used as the source for ``c''",

--- 92,98 -----
  "     \",>  Enter a right justified string or string expression.",
  "     e    Edit the current cell's numeric value.",
  "     E    Edit the current cell's string part.",
+ "     F    Assign a format to the current cell's numeric value.",
  "     x    Clear the current cell.",
  "     c    Copy the last marked cell to the current cell.",
  "     m    Mark a cell to be used as the source for ``c''",
***************
*** 192,199
  "          as ``A10'' or a range such as ``a1:b20''.",
  "     /s   Shows the currently defined range names.  Pipe output to",
  "          sort, then to less.",
! "     /u   Use this command to undefine a previously defined range",
! "          name.",
  " ",
  "     Range operations affect a rectangular region on the screen",
  "     defined by the upper left and lower right cells in the region.",

--- 194,201 -----
  "          as ``A10'' or a range such as ``a1:b20''.",
  "     /s   Shows the currently defined range names.  Pipe output to",
  "          sort, then to less.",
! "     /u   Use this command to undefine a previously defined range name.",
! "     /F   Assign a format string to a range of cells.",
  " ",
  "     Range operations affect a rectangular region on the screen",
  "     defined by the upper left and lower right cells in the region.",
*** posted/interp.c	Mon Sep 24 09:36:32 1990
--- interp.c	Mon Sep 24 09:42:28 1990
***************
*** 7,13
   *
   *              More mods Robert Bond, 12/86
   *		More mods by Alan Silverstein, 3-4/88, see list of changes.
!  *		$Revision: 6.8 $
   */
  
  #define DEBUGDTS 1		/* REMOVE ME */

--- 7,13 -----
   *
   *              More mods Robert Bond, 12/86
   *		More mods by Alan Silverstein, 3-4/88, see list of changes.
!  *		$Revision: 6.9 $
   */
  
  #define DEBUGDTS 1		/* REMOVE ME */
***************
*** 86,91
  double fn1_eval();
  double fn2_eval();
  struct	ent *firstev = (struct ent *)0;	/* first expr in the eval list */
  
  #define PI (double)3.14159265358979323846
  #define dtr(x) ((x)*(PI/(double)180.0))

--- 86,92 -----
  double fn1_eval();
  double fn2_eval();
  struct	ent *firstev = (struct ent *)0;	/* first expr in the eval list */
+ double	rint();
  
  #define PI (double)3.14159265358979323846
  #define dtr(x) ((x)*(PI/(double)180.0))
***************
*** 719,729
  	case TAN:	 return (fn1_eval( tan, eval(e->e.o.right)));
  	case DTR:	 return (dtr(eval(e->e.o.right)));
  	case RTD:	 return (rtd(eval(e->e.o.right)));
! 	case RND:	 {
! 			    double temp;
! 			    temp = eval(e->e.o.right);
! 			    return(temp-floor(temp) < 0.5 ?
! 					     floor(temp) : ceil(temp));
  			}
   	case ROUND:	{
  			    double temp = eval(e->e.o.left);

--- 720,747 -----
  	case TAN:	 return (fn1_eval( tan, eval(e->e.o.right)));
  	case DTR:	 return (dtr(eval(e->e.o.right)));
  	case RTD:	 return (rtd(eval(e->e.o.right)));
! 	case RND:
! 		if (rndinfinity)
! 		{	double temp = eval(e->e.o.right);
! 			return(temp-floor(temp) < 0.5 ?
! 					floor(temp) : ceil(temp));
! 		}
! 		else
! 			return rint(eval(e->e.o.right));
!  	case ROUND:
! 		{	int prec = (int) eval(e->e.o.right);
! 			double	scal = 1;
! 			if (0 < prec)
! 				do scal *= 10; while (0 < --prec);
! 			else if (prec < 0)
! 				do scal /= 10; while (++prec < 0);
! 
! 			if (rndinfinity)
! 			{	double temp = eval(e->e.o.left);
! 				temp *= scal;
! 				temp = ((temp-floor(temp)) < 0.5 ?
! 						floor(temp) : ceil(temp));
! 				return(temp / scal);
  			}
  			else
  				return(rint(eval(e->e.o.left) * scal) / scal);
***************
*** 725,739
  			    return(temp-floor(temp) < 0.5 ?
  					     floor(temp) : ceil(temp));
  			}
!  	case ROUND:	{
! 			    double temp = eval(e->e.o.left);
! 			    int prec = (int) eval(e->e.o.right), scal = 1;
! 			    while (prec-- > 0) scal *= 10;
! 			    temp *= scal;
! 			    temp = ((temp-floor(temp)) < 0.5 ?
! 				    floor(temp) : ceil(temp));
! 			    return(temp / scal);
! 			}
  	case FV:
  	case PV:
  	case PMT:	return(finfunc(e->op,eval(e->e.o.left),

--- 743,751 -----
  						floor(temp) : ceil(temp));
  				return(temp / scal);
  			}
! 			else
! 				return(rint(eval(e->e.o.left) * scal) / scal);
! 		}
  	case FV:
  	case PV:
  	case PMT:	return(finfunc(e->op,eval(e->e.o.left),
***************
*** 1732,1737
  }
  #endif /* EXPRTREE*/
  
  void
  hide_row(arg)
  int arg;

--- 1744,1781 -----
  }
  #endif /* EXPRTREE*/
  
+ format_cell(v1, v2, s)
+ struct ent *v1, *v2;
+ char *s;
+ {
+     register r,c;
+     register struct ent *n;
+     int maxr, maxc;
+     int minr, minc;
+ 
+     maxr = v2->row;
+     maxc = v2->col;
+     minr = v1->row;
+     minc = v1->col;
+     if (minr>maxr) r = maxr, maxr = minr, minr = r;
+     if (minc>maxc) c = maxc, maxc = minc, minc = c;
+     checkbounds(&maxr, &maxc);
+     if (minr < 0) minr = 0;
+     if (minr < 0) minr = 0;
+ 
+     FullUpdate++;
+     for (r = minr; r <= maxr; r++)
+ 	for (c = minc; c <= maxc; c++) {
+ 	    n = lookat (r, c);
+ 	    if (n->format)
+ 		xfree(n->format);
+ 	    n->format = 0;
+ 	    if (s && *s != '\0')
+ 		n->format = strcpy((char *)xmalloc(strlen(s)+1), s);
+ 	    n->flags |= is_changed;
+        }
+ }
+ 
  void
  hide_row(arg)
  int arg;
***************
*** 1780,1785
      if (v->expr)
  	efree(v, v->expr);
      v->expr = (struct enode *)0;
      v->flags |= (is_changed);
      v->flags &= ~(is_valid);
      changed++;

--- 1824,1832 -----
      if (v->expr)
  	efree(v, v->expr);
      v->expr = (struct enode *)0;
+     if (v->format)
+ 	xfree(v->format);
+     v->format = (char *)0;
      v->flags |= (is_changed);
      v->flags &= ~(is_valid);
      changed++;
***************
*** 2118,2123
      line[linelim++] = ')';
  }
  
  void
  editv (row, col)
  int row, col;

--- 2165,2182 -----
      line[linelim++] = ')';
  }
  
+ editfmt (row, col)
+ int row, col;
+ {
+     register struct ent *p;
+ 
+     p = lookat (row, col);
+     if (p->format) {
+         (void)sprintf (line, "fmt %s \"%s\"", v_name(row, col), p->format);
+ 	linelim = strlen(line);
+     }
+ }
+ 
  void
  editv (row, col)
  int row, col;
***************
*** 2167,2169
          linelim += 1;
      }
  }

--- 2226,2242 -----
          linelim += 1;
      }
  }
+ 
+ #ifdef RINT
+ double rint(d) double d;
+ {
+ /* as sent */
+ 	double fl = floor(d),  fr = d-fl;
+ 	return
+ 	    fr<0.5  ||  fr==0.5 && fl==floor(fl/2)*2   ?   fl   :   ceil(d);
+ /**/
+ /*	double fl = floor(d),  fr = d-fl;
+ 	return((fr<0.5) || ((fr==0.5) && (fl==floor(fl/2)*2)) ? fl : ceil(d));
+ */
+ }
+ #endif
*** posted/lex.c	Mon Sep 24 09:36:49 1990
--- lex.c	Mon Sep 24 09:42:31 1990
***************
*** 7,13
   *
   *              More mods Robert Bond, 12/86
   *		More mods by Alan Silverstein, 3/88, see list of changes.
!  *		$Revision: 6.8 $
   *
   */
  

--- 7,13 -----
   *
   *              More mods Robert Bond, 12/86
   *		More mods by Alan Silverstein, 3/88, see list of changes.
!  *		$Revision: 6.9 $
   *
   */
  
***************
*** 340,346
      char *ktmp;
      static char buf[1024]; /* Why do I have to do this again? */
  
!     if (tgetent(buf, getenv("TERM")) <= 0)
  	return;
  
      km[0].k_str = tgetstr("kl", &p); km[0].k_val = ctl('b');

--- 340,350 -----
      char *ktmp;
      static char buf[1024]; /* Why do I have to do this again? */
  
!     if (!(ktmp = getenv("TERM"))) {
! 	(void) fprintf(stderr, "TERM environment variable not set\n");
! 	exit (1);
!     }
!     if (tgetent(buf, ktmp) <= 0)
  	return;
  
      km[0].k_str = tgetstr("kl", &p); km[0].k_val = ctl('b');
*** posted/psc.c	Mon Sep 24 09:37:00 1990
--- psc.c	Wed Sep 12 10:32:18 1990
***************
*** 13,19
   *   -f         suppress 'format' lines in output
   *   
   *  Author: Robert Bond
!  *		$Revision: 6.8 $
   */
  
  #include <ctype.h>

--- 13,20 -----
   *   -f         suppress 'format' lines in output
   *   
   *  Author: Robert Bond
!  *  Adjustments: Jeff Buhrt and Eric Putz
!  *		$Revision: 6.10 $
   */
  
  #include <ctype.h>
***************
*** 128,134
  	case END:
  	    if(drop_format) exit(0);
  	    for (i = 0; i<maxcols; i++) {
! 		if (precision[i])
  		    (void) printf("format %s %d %d\n", coltoa(i), 
  			fwidth[i], precision[i]+1);
  	    }

--- 129,135 -----
  	case END:
  	    if(drop_format) exit(0);
  	    for (i = 0; i<maxcols; i++) {
! 		if (fwidth[i])
  		    (void) printf("format %s %d %d\n", coltoa(i), 
  			fwidth[i]+1, precision[i]);
  	    }
***************
*** 130,136
  	    for (i = 0; i<maxcols; i++) {
  		if (precision[i])
  		    (void) printf("format %s %d %d\n", coltoa(i), 
! 			fwidth[i], precision[i]+1);
  	    }
  	    exit(0);
  	case NUM:

--- 131,137 -----
  	    for (i = 0; i<maxcols; i++) {
  		if (fwidth[i])
  		    (void) printf("format %s %d %d\n", coltoa(i), 
! 			fwidth[i]+1, precision[i]);
  	    }
  	    exit(0);
  	case NUM:
***************
*** 154,163
  	    while (*p) {
  		p++; i++; j++;
  	    }
! 	    if (precision[effc] < j)
! 		precision[effc] = j;
! 	    if (fwidth[effc] < i)
! 		fwidth[effc] = i;
  	    break;
  	case ALPHA:
  	    first = 0;

--- 155,178 -----
  	    while (*p) {
  		p++; i++; j++;
  	    }
! 	    {   int	ow, nw;
! 
! 		ow = fwidth[effc] - precision[effc];
! 		if (precision[effc] < j)
! 			precision[effc] = j;
! 	
! 		if (fwidth[effc] < i)
! 			fwidth[effc] = i;
! 
! 		/* now make sure:
! 		 *	1234.567890 (format 11 6)
! 		 *	1234567.890 (format 11 3)
! 		 *	both show (format 14 6)
! 		 *		(really it uses 15 6 to separate columns)
! 		 */
! 		if ((nw = i - j) > ow)
! 			fwidth[effc] += nw - (fwidth[effc] - precision[effc]);
! 	    }
  	    break;
  	case ALPHA:
  	    first = 0;
***************
*** 172,179
  		}
  	    }
  	    i = strlen(token);
! 	    if (i > precision[effc])
! 		precision[effc] = i;
  	    break;
  	case SPACE:
  	    if (first && strip_delim)

--- 187,194 -----
  		}
  	    }
  	    i = strlen(token);
! 	    if (i > fwidth[effc])
! 		fwidth[effc] = i;
  	    break;
  	case SPACE:
  	    if (first && strip_delim)
***************
*** 214,219
  {
      register int c;
      register char *p;
  
      p = token;
      c = getchar();

--- 229,235 -----
  {
      register int c;
      register char *p;
+     register int founddigit;
  
      p = token;
      c = getchar();
***************
*** 251,256
  
      p = token;
      c = *p;
      if (isdigit(c) || c == '.' || c == '-' || c == '+') {
  	while(isdigit(c) || c == '.' || c == '-' || c == '+' || c == 'e'
  	    || c == 'E') {

--- 267,273 -----
  
      p = token;
      c = *p;
+     founddigit = 0;
      if (isdigit(c) || c == '.' || c == '-' || c == '+') {
  	while(isdigit(c) || c == '.' || c == '-' || c == '+' || c == 'e'
  	    || c == 'E') {
***************
*** 254,259
      if (isdigit(c) || c == '.' || c == '-' || c == '+') {
  	while(isdigit(c) || c == '.' || c == '-' || c == '+' || c == 'e'
  	    || c == 'E') {
  		c = *p++;
  	}
  	if (c == 0)

--- 271,278 -----
      if (isdigit(c) || c == '.' || c == '-' || c == '+') {
  	while(isdigit(c) || c == '.' || c == '-' || c == '+' || c == 'e'
  	    || c == 'E') {
+ 		if (isdigit(c)) 
+ 		    founddigit = 1;
  		c = *p++;
  	}
  	if (c == 0 && founddigit)
***************
*** 256,262
  	    || c == 'E') {
  		c = *p++;
  	}
! 	if (c == 0)
  	    return(NUM);
  	else
  	    return(ALPHA);

--- 275,281 -----
  		    founddigit = 1;
  		c = *p++;
  	}
! 	if (c == 0 && founddigit)
  	    return(NUM);
  	else
  	    return(ALPHA);
*** posted/sc.c	Mon Sep 24 09:37:52 1990
--- sc.c	Mon Sep 24 09:42:47 1990
***************
*** 8,14
   *              More mods Robert Bond, 12/86
   *		More mods by Alan Silverstein, 3-4/88, see list of changes.
   *		Currently supported by pur-phy!sawmill!buhrt (Jeff Buhrt)
!  *		$Revision: 6.8 $
   *
   */
  

--- 8,14 -----
   *              More mods Robert Bond, 12/86
   *		More mods by Alan Silverstein, 3-4/88, see list of changes.
   *		Currently supported by pur-phy!sawmill!buhrt (Jeff Buhrt)
!  *		$Revision: 6.9 $
   *
   */
  
***************
*** 78,83
  int  autocalc = 1 ;	/* 1 to calculate after each update */
  int  calc_order = BYROWS;
  int  tbl_style = 0;	/* headers for T command output */
  
  int  lastmx, lastmy;	/* Screen address of the cursor */
  int  lastcol;		/* Spreadsheet Column the cursor was in last */

--- 78,84 -----
  int  autocalc = 1 ;	/* 1 to calculate after each update */
  int  calc_order = BYROWS;
  int  tbl_style = 0;	/* headers for T command output */
+ int  rndinfinity = 0;
  
  int  lastmx, lastmy;	/* Screen address of the cursor */
  int  lastcol;		/* Spreadsheet Column the cursor was in last */
***************
*** 117,122
  	(*pp)->flags = 0;
  	(*pp)->expr = (struct enode *)0;
  	(*pp)->v = (double) 0.0;
  	(*pp)->evnext = (struct ent *)0;
      }
      return *pp;

--- 118,124 -----
  	(*pp)->flags = 0;
  	(*pp)->expr = (struct enode *)0;
  	(*pp)->v = (double) 0.0;
+ 	(*pp)->format = (char *)0;
  	(*pp)->evnext = (struct ent *)0;
      }
      return *pp;
***************
*** 392,398
  
  		    if ((*pp) -> flags & is_valid) {
  			char field[FBUFLEN];
! 			(void)sprintf(field,"%*.*f", fwidth[col], precision[col], (*pp)->v);
  			if(strlen(field) > fwidth[col]) {
  			    for(i = 0; i<fwidth[col]; i++)
  				(void)addch('*');

--- 394,406 -----
  
  		    if ((*pp) -> flags & is_valid) {
  			char field[FBUFLEN];
! 			if ((*pp) -> format) {
! 			    (void)format((*pp) -> format, (*pp) -> v, field,
! 					 sizeof(field));
! 			} else {
! 			    (void)sprintf(field,"%*.*f", fwidth[col],
! 					  precision[col], (*pp)->v);
! 			}
  			if(strlen(field) > fwidth[col]) {
  			    for(i = 0; i<fwidth[col]; i++)
  				(void)addch('*');
***************
*** 397,402
  			    for(i = 0; i<fwidth[col]; i++)
  				(void)addch('*');
  			} else {
  			    (void)addstr(field);
  			}
  		    }

--- 405,412 -----
  			    for(i = 0; i<fwidth[col]; i++)
  				(void)addch('*');
  			} else {
+ 			    for(i = 0; i < fwidth[col] - strlen(field); i++)
+ 				(void)addch(' ');
  			    (void)addstr(field);
  			}
  		    }
***************
*** 803,809
  
  		case ctl('t'):
  		    error(
! "Toggle:  a:auto  c:cell  e:ext funcs  n:numeric  t:top  x:encrypt  $:pre-scale");
  		    (void) refresh();
  
  		    switch (nmgetch()) {

--- 813,819 -----
  
  		case ctl('t'):
  		    error(
! "Toggle: a:auto  c:cell  e:ext funcs  n:numeric  t:top  x:encrypt  $:pre-scale");
  		    (void) refresh();
  
  		    switch (nmgetch()) {
***************
*** 876,881
  
  			temp = strcpy(xmalloc((unsigned)(strlen(line)+1)),line);
  			templim = linelim;
  			editexp(currow,curcol);
  			temp1= strcpy(xmalloc((unsigned)(strlen(line)+1)),line);
  			strcpy(line, temp);

--- 886,892 -----
  
  			temp = strcpy(xmalloc((unsigned)(strlen(line)+1)),line);
  			templim = linelim;
+ 			linelim = 0;		/* reset line to empty	*/
  			editexp(currow,curcol);
  			temp1= strcpy(xmalloc((unsigned)(strlen(line)+1)),line);
  			strcpy(line, temp);
***************
*** 1033,1039
  
  		case '/':
  		    error (
! "Range:  x:erase  v:value  c:copy  f:fill  d:define  s:show  u:undefine");
  		    (void) refresh();
  
  		    switch (nmgetch()) {

--- 1044,1050 -----
  
  		case '/':
  		    error (
! "Range:  x:erase  v:value  c:copy  f:fill  d:define  s:show  u:undefine  F:fmt");
  		    (void) refresh();
  
  		    switch (nmgetch()) {
***************
*** 1096,1101
  			}
  			else error("No ranges defined");
  			break;
  			
  		    case ESC:
  		    case ctl('g'):

--- 1107,1118 -----
  			}
  			else error("No ranges defined");
  			break;
+ 		    case 'F':
+ 			(void) sprintf(line, "fmt [range \"format\"] ");
+ 			linelim = strlen(line);
+ 			startshow();
+ 			insert_mode();
+ 			break;
  			
  		    case ESC:
  		    case ctl('g'):
***************
*** 1283,1288
  		    linelim = strlen (line);
  		    insert_mode();
  		    break;
  		case 'g':
  		    (void) sprintf (line, "goto [v] ");
  		    linelim = strlen (line);

--- 1300,1311 -----
  		    linelim = strlen (line);
  		    insert_mode();
  		    break;
+ 		case 'F':
+ 		    (void) sprintf(line, "fmt [format] %s \"",
+ 				   v_name(currow, curcol));
+ 		    linelim = strlen(line);
+ 		    insert_mode();
+ 		    break;
  		case 'g':
  		    (void) sprintf (line, "goto [v] ");
  		    linelim = strlen (line);
***************
*** 1327,1333
  		    break;
  		case 'S':	/* set options */
  		    (void) sprintf (line, "set ");
! 		    error("Options: byrows, bycols, iterations=n, tblstyle=(0|tbl|latex|tex)");
  		    linelim = strlen (line);
  		    insert_mode();
  		    break;

--- 1350,1356 -----
  		    break;
  		case 'S':	/* set options */
  		    (void) sprintf (line, "set ");
! 		    error("Options: byrows, bycols, iterations=n, tblstyle=(0|tbl|latex|tex), rndinfinity");
  		    linelim = strlen (line);
  		    insert_mode();
  		    break;
*** posted/sc.doc	Mon Sep 24 09:38:48 1990
--- sc.doc	Mon Sep 24 09:48:12 1990
***************
*** 15,21
  .\" - TPs use default indent except for function names, then 18.
  .\" - Smallify uppercase strings.
  .\" - Avoid passive voice and third person.
! .\" $Revision: 6.8 $
  .\"
  .TH PNAME 1
  .SH NAME

--- 15,21 -----
  .\" - TPs use default indent except for function names, then 18.
  .\" - Smallify uppercase strings.
  .\" - Avoid passive voice and third person.
! .\" $Revision: 6.9 $
  .\"
  .TH PNAME 1
  .SH NAME
***************
*** 61,66
  and/or an expression (formula)
  which evaluates to a numeric value or label string,
  often based on other cell values.
  .\" ----------
  .PP
  Options are:

--- 61,70 -----
  and/or an expression (formula)
  which evaluates to a numeric value or label string,
  often based on other cell values.
+ .LP
+ For a on-line tutorial, type the command:
+ .IP
+ pname #LIBDIR#/tutorial.sc
  .\" ----------
  .SH OPTIONS
  .\" ----------
***************
*** 62,69
  which evaluates to a numeric value or label string,
  often based on other cell values.
  .\" ----------
! .PP
! Options are:
  .\" ----------
  .TP
  .B \-c

--- 66,72 -----
  .IP
  pname #LIBDIR#/tutorial.sc
  .\" ----------
! .SH OPTIONS
  .\" ----------
  .TP
  .B \-c
***************
*** 324,329
  .TP
  .BR toprow /  !toprow
  Set/clear top row display mode.
  .RE
  .\" ==========
  .SS "Cursor Control Commands"

--- 327,337 -----
  .TP
  .BR toprow /  !toprow
  Set/clear top row display mode.
+ .TP
+ .BR rndinfinity /  !rndinfinity
+ default: round-to-even (banker's round), *.5 will round to the closest even
+ number; doing a 'set rndinfinity' will round *.5 up to the next integer
+ (rounding to infinity).
  .RE
  .\" ==========
  .SS "Cursor Control Commands"
***************
*** 504,509
  .B >
  Enter a label string into the current cell
  to be flushed right against the right edge of the cell.
  .PD
  .\" ----------
  .PP

--- 512,518 -----
  .B >
  Enter a label string into the current cell
  to be flushed right against the right edge of the cell.
+ .\" ----------
  .PD
  .TP
  .B F
***************
*** 505,510
  Enter a label string into the current cell
  to be flushed right against the right edge of the cell.
  .PD
  .\" ----------
  .PP
  Strings you enter must start with ".

--- 514,601 -----
  to be flushed right against the right edge of the cell.
  .\" ----------
  .PD
+ .TP
+ .B F
+ Enter a format string into the current cell.  This format string
+ overrides the precision specified with the ``f'' command.
+ The format only applies to numeric values.  The following 
+ characters can be used to build a format string:
+ .RS
+ .TP
+ .BR #
+ Digit placeholder.  If the number has fewer digits on either
+ side of the decimal point than  there are `#' characters in
+ the format, the extra `#' characters are ignored.  The number
+ is rounded to the number of digit placeholders as there are
+ to the right of the decimal point.  If there are more digits
+ in the number than there are digit placeholders on the left
+ side of the decimal point, then those digits are displayed.
+ .TP
+ .BR 0
+ Digit placeholder.
+ Same as for `#' except that the number
+ is padded with zeroes on either side of the decimal point.
+ The number of zeroes used in padding is determined by the
+ number of digit placeholders after the `0' for digits on
+ the left side of the decimal point and by the number of
+ digit placeholders before the `0' for digits on the right
+ side of the decimal point.
+ .TP
+ .BR .
+ Decimal point.
+ Determines how many digits are placed on
+ the right and left sides of the decimal point in the number.
+ Note that numbers smaller than 1 will begin with a decimal
+ point if the left side of the decimal point contains only
+ a `#' digit placeholder.  Use a `0' placeholder to get a
+ leading zero in decimal formats.
+ .TP
+ .BR %
+ Percentage.
+ For each `%' character in the format, the actual
+ number gets multiplied by 100 (only for purposes of formatting
+ -- the original number is left unmodified) and the `%' character
+ is placed in the same position as it is in the format.
+ .TP
+ .BR ,
+ Thousands separator.
+ The presence of a `,' in the format
+ (multiple commas are treated as one) will cause the number
+ to be formatted with a `,' separating each set of three digits
+ in the integer part of the number with numbering beginning
+ from the right end of the integer.
+ .TP
+ .BR \\
+ Quote.
+ This character causes the next character to be
+ inserted into the formatted string directly with no
+ special interpretation.
+ .TP
+ .BR E-\ E+\ e-\ e+
+ Scientific format.
+ Causes the number to formatted in scientific
+ notation.  The case of the `E' or `e' given is preserved.  If
+ the format uses a `+', then the sign is always given for the
+ exponent value.  If the format uses a `-', then the sign is
+ only given when the exponent value is negative.  Note that if
+ there is no digit placeholder following the `+' or `-', then
+ that part of the formatted number is left out.  In general,
+ there should be one or more digit placeholders after the `+'
+ or `-'.
+ .TP
+ .BR ;
+ Format selector.
+ Use this character to separate the format
+ into two distinct formats.  The format to the left of the
+ `;' character will be used if the number given is zero or
+ positive.  The format to the right of the `;' character is
+ used if the number given is negative.
+ .RE
+ .IP
+ Some example formats are integer (``0'' or ``#''),
+ fixed (``0.00''), percentage (``0%'' or ``0.00%''),
+ scientific (``0.00E+00''),
+ and currency (``$#,0.00;($#,0.00)'').
  .\" ----------
  .PP
  Strings you enter must start with ".
***************
*** 520,525
  This is identical to ``=''
  except that the command line starts out containing
  the old numeric value or expression associated with the cell.
  .\" ----------
  .TP
  .B E

--- 611,662 -----
  This is identical to ``=''
  except that the command line starts out containing
  the old numeric value or expression associated with the cell.
+ The editing in this mode is vi-like.
+ 	^h	move back a character
+ 	+	forward through history (neat)	(same as j)
+ 	-	backward through history (neat)	(same as k)
+ 	ESC	done editing
+ 	TAB	mark && append a range (ex: A0:A0)
+ 		TAB, move around w/i a range, TAB (appends range string)
+ 	CR	save
+ 	$	goto last col
+ 	.	insert current dot buffer
+ 	/	search for a string in the history
+ 		ESC	edit the you typed
+ 		CR	search
+ 		^h	backspace
+ 	0	goto column 0
+ 	D	Delete to send
+ 	I	Insert at column 0
+ 		ESC	revert back to edit mode
+ 	R	Replace mode
+ 		ESC	revert back to edit mode
+ 	X	delete the char to the left
+ 	a	append mode
+ 	b	move back a word
+ 	c	change mode
+ 	d	delete ...
+ 		b	back word
+ 		f	forward (right)
+ 		h	back char
+ 		l	forward
+ 		t	delete forward up to a given char (next char typed)
+ 		w	delete next word forward
+ 	f	find the next char typed
+ 	h	move left a char
+ 	i	insert mode
+ 		ESC	revert back to edit mode
+ 	j	forward through history (neat)	(same as +)
+ 	k	backward	"	"	(same as +)
+ 	l	move right a char
+ 	n	continue search
+ 	q	stop editing
+ 	r	replace char
+ 	t	goto a char
+ 	u	undo
+ 	w	forward a word
+ 	x	delete the current char (moving to the right)
+ 
  .\" ----------
  .TP
  .B E
***************
*** 527,532
  This is identical to ``<'', ``"'', or ``>''
  except that the command line starts out containing
  the old string value or expression associated with the cell.
  .\" ----------
  .PP
  To enter and edit a cell's number part, use the ``='' and

--- 664,672 -----
  This is identical to ``<'', ``"'', or ``>''
  except that the command line starts out containing
  the old string value or expression associated with the cell.
+ SEE
+ .B e
+ ABOVE.
  .\" ----------
  .PP
  To enter and edit a cell's number part, use the ``='' and
***************
*** 909,914
  .TP
  .B /u
  Use this command to undefine a previously defined range name.
  .\" ==========
  .SS "Miscellaneous Commands"
  .\" ----------

--- 1049,1058 -----
  .TP
  .B /u
  Use this command to undefine a previously defined range name.
+ .TP
+ .B /F
+ Use this command to assign a value format string (see the ``F''
+ cell entry commmand) to a range of cells.
  .\" ==========
  .SS "Miscellaneous Commands"
  .\" ----------
***************
*** 1378,1383
  Round
  .I e
  to the nearest integer.
  .TP 18
  .BR @round (e,n)
  Round

--- 1522,1529 -----
  Round
  .I e
  to the nearest integer.
+ default: round-to-even (banker's round), *.5 will round to the closest even
+ number; 'set rndinfinity' will round *.5 up to the next integer.
  .TP 18
  .BR @round (e,n)
  Round
***************
*** 1385,1390
  to
  .I n
  decimal places.
  .\" ----------
  .TP 18
  .BR @abs (e)

--- 1531,1539 -----
  to
  .I n
  decimal places.
+ n may be positive to round off the right side of the decimal,
+ and negative to round off the left side. See @rnd(e) above for rounding
+ types.
  .\" ----------
  .TP 18
  .BR @abs (e)
***************
*** 1778,1794
  with (perhaps) recourse to relaxation should be implemented.
  .\" ----------
  .PP
- Editing is crude.
- All you can do is backspace over and retype text to be altered.
- There is no easy way to switch a leftstring to a rightstring or vice versa.
- Of course, you can always write the spreadsheet to a file with
- .IR Put ,
- edit it by calling an editor on the file with ``!'',
- and read it back with
- .I Get
- -- if you are comfortable editing spreadsheet files.
- .\" ----------
- .PP
  Only one previous value is saved from any call of
  .IR @ext ().
  If it is used more than once in a spreadsheet

--- 1927,1932 -----
  with (perhaps) recourse to relaxation should be implemented.
  .\" ----------
  .PP
  Only one previous value is saved from any call of
  .IR @ext ().
  If it is used more than once in a spreadsheet
***************
*** 1808,1810
  .PP
  Many commands give no indication (a message or beep) if they have null effect.
  Some should give confirmation of their action, but they don't.

--- 1946,1983 -----
  .PP
  Many commands give no indication (a message or beep) if they have null effect.
  Some should give confirmation of their action, but they don't.
+ .SH AUTHORS
+ This is a much modified version of a public domain spread sheet
+ originally authored by James Gosling,
+ and subsequently modified and posted to USENET by Mark Weiser under the name
+ .IR vc .
+ The program was subsequently renamed
+ .IR sc ,
+ and further modified by numerous contributors,
+ Jeff Buhrt
+ of Grauel Enterprises, Inc.
+ ({pur-phy (aka: newton.physics.purdue.edu), sequent}!sawmill!buhrt)
+ and Robert Bond of Sequent,
+ prominent among them.
+ Other contributors include:
+ Gregory Bond,
+ Peter Brower,
+ John Campbell,
+ Lawrence Cipriani,
+ Chris Cole,
+ Glen Ditchfield
+ Sam Drake,
+ Kurt Horton,
+ Peter King,
+ Dave Lewis, 
+ Rick Linck,
+ Soren Lundsgaard,
+ Tad Mannes,
+ Rob McMahon,
+ Marius Olafsson,
+ Rick Perry,
+ R. P. C. Rodgers,
+ Alan Silverstein,
+ and
+ Andy Valencia.
+ .\" end of man page
*** posted/sc.h	Mon Sep 24 09:38:59 1990
--- sc.h	Mon Sep 24 09:42:50 1990
***************
*** 6,12
   *			University of Maryland
   *		R. Bond  12/86
   *		More mods by Alan Silverstein, 3-4/88, see list of changes.
!  *		$Revision: 6.8 $
   *
   */
  

--- 6,12 -----
   *			University of Maryland
   *		R. Bond  12/86
   *		More mods by Alan Silverstein, 3-4/88, see list of changes.
!  *		$Revision: 6.9 $
   *
   */
  
***************
*** 68,73
      struct ent *next;		/* next deleted ent */
      struct ent *evnext;		/* next ent w/ a object to eval */
      struct ent *evprev;		/* prev ent w/ a object to eval */
  };
  
  struct range {

--- 68,74 -----
      struct ent *next;		/* next deleted ent */
      struct ent *evnext;		/* next ent w/ a object to eval */
      struct ent *evprev;		/* prev ent w/ a object to eval */
+     char *format;
  };
  
  struct range {
***************
*** 338,343
  extern	int loading;
  extern	int getrcqual;
  extern	int tbl_style;
  extern	char *progname;
  
  #if BSD42 || SYSIII

--- 339,345 -----
  extern	int loading;
  extern	int getrcqual;
  extern	int tbl_style;
+ extern	int rndinfinity;
  extern	char *progname;
  
  #if BSD42 || SYSIII
*** posted/version.c	Mon Sep 24 09:39:08 1990
--- version.c	Mon Sep 24 09:45:54 1990
***************
*** 4,7
   * The part after the first colon, except the last char, appears on the screen.
   */
  
! char *rev = "$Revision: 6.8 $";

--- 4,7 -----
   * The part after the first colon, except the last char, appears on the screen.
   */
  
! char *rev = "$Revision: 6.9 $";
*** posted/vi.c	Mon Sep 24 09:39:19 1990
--- vi.c	Mon Sep 24 09:42:58 1990
***************
*** 1,7
  /*	SC	A Spreadsheet Calculator
   *
   *	One line vi emulation
!  *	$Revision: 6.8 $
   */
  
  

--- 1,7 -----
  /*	SC	A Spreadsheet Calculator
   *
   *	One line vi emulation
!  *	$Revision: 6.9 $
   */
  
  
***************
*** 36,42
  #define EDIT_MODE       1	/* Edit mode */
  #define REP_MODE        2	/* Replace mode */
  #define SEARCH_MODE	3	/* Get arguments for '/' command */
!  
  static int mode = INSERT_MODE;
  static char *history[HISTLEN];
  static int histp = -1;

--- 36,44 -----
  #define EDIT_MODE       1	/* Edit mode */
  #define REP_MODE        2	/* Replace mode */
  #define SEARCH_MODE	3	/* Get arguments for '/' command */
! 
! #define	DOTLEN		200
! 
  static int mode = INSERT_MODE;
  static char *history[HISTLEN];
  static int histp = -1;
***************
*** 43,49
  static char *last_search;
  static char *undo_line;
  static int undo_lim;
! static char dotb[100];
  static int doti = 0;
  static int do_dot = 0;
  

--- 45,51 -----
  static char *last_search;
  static char *undo_line;
  static int undo_lim;
! static char dotb[DOTLEN];
  static int doti = 0;
  static int do_dot = 0;
  
***************
*** 47,52
  static int doti = 0;
  static int do_dot = 0;
  
  void
  write_line(c)
  int c;

--- 49,55 -----
  static int doti = 0;
  static int do_dot = 0;
  
+ 
  void
  write_line(c)
  int c;
***************
*** 116,121
      mode = EDIT_MODE;
      mode_ind = 'e';
      histp = -1;
      if (line[linelim] == '\0')
  	linelim = back_line();
  }

--- 119,126 -----
      mode = EDIT_MODE;
      mode_ind = 'e';
      histp = -1;
+     if (linelim < 0)	/* -1 says stop editing, ...so we still aren't */
+ 	return;
      if (line[linelim] == '\0')
  	linelim = back_line();
  }
***************
*** 151,158
      if (do_dot)
  	return;
  
!     dotb[doti++] = c;
!     dotb[doti] = 0;
  }
  
  dotcmd()

--- 156,166 -----
      if (do_dot)
  	return;
  
!     if (doti < DOTLEN-1)
!     {
! 	dotb[doti++] = c;
! 	dotb[doti] = NULL;
!     }
  }
  
  static int dotcalled = 0;
***************
*** 155,160
      dotb[doti] = 0;
  }
  
  dotcmd()
  {
      int c;

--- 163,170 -----
      }
  }
  
+ static int dotcalled = 0;
+ 
  dotcmd()
  {
      int c;
***************
*** 159,164
  {
      int c;
  
      do_dot = 1;
      doti = 0;
      while(dotb[doti] != 0) {

--- 169,176 -----
  {
      int c;
  
+     if (dotcalled)	/* stop recursive calling of dotcmd() */
+ 	return;
      do_dot = 1;
      doti = 0;
      while(dotb[doti] != 0) {
***************
*** 163,168
      doti = 0;
      while(dotb[doti] != 0) {
  	c = dotb[doti++];
  	write_line(c);
      }
      do_dot = 0;

--- 175,181 -----
      doti = 0;
      while(dotb[doti] != 0) {
  	c = dotb[doti++];
+ 	dotcalled = 1;
  	write_line(c);
      }
      do_dot = 0;
***************
*** 167,172
      }
      do_dot = 0;
      doti = 0;
  }
  
  vigetch()

--- 180,186 -----
      }
      do_dot = 0;
      doti = 0;
+     dotcalled = 0;
  }
  
  vigetch()
***************
*** 351,356
  {
      register int i, len;
  
      len = strlen(line);
      for (i = len; i >= linelim; --i)
  	line[i+1] = line[i];

--- 365,374 -----
  {
      register int i, len;
  
+     if (linelim < 0)
+     {	*line = 0;
+ 	linelim = 0;
+     }
      len = strlen(line);
      for (i = len; i >= linelim; --i)
  	line[i+1] = line[i];
***************
*** 380,385
  {
      int c;
  
      c = vigetch();
      if (line[linelim] != 0) {
      	line[linelim] = c;

--- 398,407 -----
  {
      int c;
  
+     if (linelim < 0)
+     {	linelim = 0;
+ 	*line = 0;
+     }
      c = vigetch();
      if (line[linelim] != 0) {
      	line[linelim] = c;
***************
*** 393,398
  {
      register int len;
  
      len = strlen(line);
      line[linelim++] = c;
      if (linelim > len)

--- 415,424 -----
  {
      register int len;
  
+     if (linelim < 0)
+     {	linelim = 0;
+ 	*line = 0;
+     }
      len = strlen(line);
      line[linelim++] = c;
      if (linelim > len)
*** posted/vmtbl.c	Mon Sep 24 09:39:25 1990
--- vmtbl.c	Mon Sep 24 09:43:00 1990
***************
*** 49,55
  	if (*colp < 0) 
  		*colp = 0;
  	else if (*colp >= maxcols)
! 	{	if (!growtbl(GROWCOL, 0, *colp));
  			*colp = maxcols-1;
  	}
  }

--- 49,55 -----
  	if (*colp < 0) 
  		*colp = 0;
  	else if (*colp >= maxcols)
! 	{	if (!growtbl(GROWCOL, 0, *colp))
  			*colp = maxcols-1;
  	}
  }

sjg@melb.bull.oz.au (Simon J Gerraty) (11/02/90)

Sc6.8 was posted some weeks ago now.
I got all of it except part 01.
Having tried several times to send e-mail to the author without
success, I sat back and waited to see if it would show up or
someone would announce an archive site etc etc.
Now we have seen 6.8->6.9->6.10 and I am still stuck with 4.1

So, could some kind soul _please_ e-mail me part 01 of sc6.8.
Or the completed 6.10 would be nice :-)
Also what about an FTP site for sc?


-- 
Simon J. Gerraty		<sjg@melb.bull.oz.au>

#include <disclaimer,_witty_comment>