xref: /original-bsd/contrib/sc/cmds.c (revision 7f3e12df)
1 /*	SC	A Spreadsheet Calculator
2  *		Command routines
3  *
4  *		original by James Gosling, September 1982
5  *		modifications by Mark Weiser and Bruce Israel,
6  *			University of Maryland
7  *
8  *              More mods Robert Bond, 12/86
9  *
10  *		$Revision: 6.8 $
11  */
12 
13 #include <curses.h>
14 #if defined(BSD42) || defined(BSD43)
15 #include <sys/file.h>
16 #else
17 #include <fcntl.h>
18 #endif
19 #include "sc.h"
20 #include <signal.h>
21 #include <errno.h>
22 
23 #ifdef BSD42
24 #include <strings.h>
25 #else
26 #ifndef SYSIII
27 #include <string.h>
28 #endif
29 #endif
30 
31 #ifdef SYSV3
32 extern void exit();
33 #else
34 extern int exit();
35 #endif
36 
37 extern	int	errno;
38 
39 #define DEFCOLDELIM ':'
40 
41 void
42 duprow()
43 {
44     if (currow >= maxrows - 1 || maxrow >= maxrows - 1) {
45 	if (!growtbl(GROWROW, 0, 0))
46 		return;
47     }
48     modflg++;
49     currow++;
50     openrow (currow);
51     for (curcol = 0; curcol <= maxcol; curcol++) {
52 	register struct ent *p = *ATBL(tbl, currow - 1, curcol);
53 	if (p) {
54 	    register struct ent *n;
55 	    n = lookat (currow, curcol);
56 	    (void)copyent ( n, p, 1, 0);
57 	}
58     }
59     for (curcol = 0; curcol <= maxcol; curcol++) {
60 	register struct ent *p = *ATBL(tbl, currow, curcol);
61 	if (p && (p -> flags & is_valid) && !p -> expr)
62 	    break;
63     }
64     if (curcol > maxcol)
65 	curcol = 0;
66 }
67 
68 void
69 dupcol()
70 {
71     if (curcol >= maxcols - 1 || maxcol >= maxcols - 1) {
72 	if (!growtbl(GROWCOL, 0, 0))
73 		return;
74     }
75     modflg++;
76     curcol++;
77     opencol (curcol, 1);
78     for (currow = 0; currow <= maxrow; currow++) {
79 	register struct ent *p = *ATBL(tbl, currow, curcol - 1);
80 	if (p) {
81 	    register struct ent *n;
82 	    n = lookat (currow, curcol);
83 	    copyent ( n, p, 0, 1);
84 	}
85     }
86     for (currow = 0; currow <= maxrow; currow++) {
87 	register struct ent *p = *ATBL(tbl, currow, curcol);
88 	if (p && (p -> flags & is_valid) && !p -> expr)
89 	    break;
90     }
91     if (currow > maxrow)
92 	currow = 0;
93 }
94 
95 void
96 insertrow(arg)
97 register int arg;
98 {
99     while (--arg>=0) openrow (currow);
100 }
101 
102 void
103 deleterow(arg)
104 register int arg;
105 {
106     flush_saved();
107     erase_area(currow, 0, currow + arg - 1, maxcol);
108     currow += arg;
109     while (--arg>=0) closerow (--currow);
110     sync_refs();
111 }
112 
113 void
114 rowvalueize(arg)
115 register int arg;
116 {
117     valueize_area(currow, 0, currow + arg - 1, maxcol);
118 }
119 
120 void
121 colvalueize(arg)
122 register int arg;
123 {
124     valueize_area(0, curcol, maxrow, curcol + arg - 1);
125 }
126 
127 void
128 erase_area(sr, sc, er, ec)
129 int sr, sc, er, ec;
130 {
131     register int r, c;
132     register struct ent **pp;
133 
134     if (sr > er) {
135 	r = sr; sr = er; er= r;
136     }
137 
138     if (sc > ec) {
139 	c = sc; sc = ec; ec= c;
140     }
141 
142     if (sr < 0)
143 	sr = 0;
144     if (sc < 0)
145 	sc = 0;
146     checkbounds(&er, &ec);
147 
148     for (r = sr; r <= er; r++) {
149 	for (c = sc; c <= ec; c++) {
150 	    pp = ATBL(tbl, r, c);
151 	    if (*pp) {
152 		free_ent(*pp);
153 		*pp = (struct ent *)0;
154 	    }
155 	}
156     }
157 }
158 
159 void
160 valueize_area(sr, sc, er, ec)
161 int sr, sc, er, ec;
162 {
163     register int r, c;
164     register struct ent *p;
165 
166     if (sr > er) {
167 	r = sr; sr = er; er= r;
168     }
169 
170     if (sc > ec) {
171 	c = sc; sc = ec; ec= c;
172     }
173 
174     if (sr < 0)
175 	sr = 0;
176     if (sc < 0)
177 	sc = 0;
178     checkbounds(&er, &ec);
179 
180     for (r = sr; r <= er; r++) {
181 	for (c = sc; c <= ec; c++) {
182 	    p = *ATBL(tbl, r, c);
183 	    if (p && p->expr) {
184 		efree(p, p->expr);
185 		p->expr = (struct enode *)0;
186 		p->flags &= ~is_strexpr;
187 	    }
188 	}
189     }
190 
191 }
192 
193 void
194 pullcells(to_insert)
195 int to_insert;
196 {
197     register struct ent *p, *n;
198     register int deltar, deltac;
199     int minrow, mincol;
200     int mxrow, mxcol;
201     int numrows, numcols;
202 
203     if (! to_fix)
204     {
205 	error ("No data to pull");
206 	return;
207     }
208 
209     minrow = maxrows;
210     mincol = maxcols;
211     mxrow = 0;
212     mxcol = 0;
213 
214     for (p = to_fix; p; p = p->next) {
215 	if (p->row < minrow)
216 	    minrow = p->row;
217 	if (p->row > mxrow)
218 	    mxrow = p->row;
219 	if (p->col < mincol)
220 	    mincol = p->col;
221 	if (p->col > mxcol)
222 	    mxcol = p->col;
223     }
224 
225     numrows = mxrow - minrow + 1;
226     numcols = mxcol - mincol + 1;
227     deltar = currow - minrow;
228     deltac = curcol - mincol;
229 
230     if (to_insert == 'r') {
231 	insertrow(numrows);
232 	deltac = 0;
233     } else if (to_insert == 'c') {
234 	opencol(curcol, numcols);
235 	deltar = 0;
236     }
237 
238     FullUpdate++;
239     modflg++;
240 
241     for (p = to_fix; p; p = p->next) {
242 	n = lookat (p->row + deltar, p->col + deltac);
243 	(void) clearent(n);
244 	copyent( n, p, deltar, deltac);
245 	n -> flags = p -> flags & ~is_deleted;
246     }
247 }
248 
249 void
250 colshow_op()
251 {
252     register int i,j;
253     for (i=0; i<maxcols; i++)
254 	if (col_hidden[i])
255 	    break;
256     for(j=i; j<maxcols; j++)
257 	if (!col_hidden[j])
258 	    break;
259     j--;
260     if (i>=maxcols)
261 	error ("No hidden columns to show");
262     else {
263 	(void) sprintf(line,"show %s:", coltoa(i));
264 	(void) sprintf(line + strlen(line),"%s",coltoa(j));
265 	linelim = strlen (line);
266     }
267 }
268 
269 void
270 rowshow_op()
271 {
272     register int i,j;
273     for (i=0; i<maxrows; i++)
274 	if (row_hidden[i])
275 	    break;
276     for(j=i; j<maxrows; j++)
277 	if (!row_hidden[j]) {
278 	    break;
279 	}
280     j--;
281 
282     if (i>=maxrows)
283 	error ("No hidden rows to show");
284     else {
285 	(void) sprintf(line,"show %d:%d", i, j);
286         linelim = strlen (line);
287     }
288 }
289 
290 /*
291  * Given a row/column command letter, emit a small menu, then read a qualifier
292  * character for a row/column command and convert it to 'r' (row), 'c'
293  * (column), or 0 (unknown).  If ch is 'p', an extra qualifier 'm' is allowed.
294  */
295 
296 int
297 get_rcqual (ch)
298     int ch;
299 {
300     error ("%sow/column:  r: row  c: column%s",
301 
302 	    (ch == 'i') ? "Insert r" :
303 	    (ch == 'a') ? "Append r" :
304 	    (ch == 'd') ? "Delete r" :
305 	    (ch == 'p') ? "Pull r" :
306 	    (ch == 'v') ? "Values r" :
307 	    (ch == 'z') ? "Zap r" :
308 	    (ch == 's') ? "Show r" : "R",
309 
310 	    (ch == 'p') ? "  m: merge" : "");
311 
312     (void) refresh();
313 
314     switch (nmgetch())
315     {
316 	case 'r':
317 	case 'l':
318 	case 'h':
319 	case ctl('f'):
320 	case ctl('b'):	return ('r');
321 
322 	case 'c':
323 	case 'j':
324 	case 'k':
325 	case ctl('p'):
326 	case ctl('n'):	return ('c');
327 
328 	case 'm':	return ((ch == 'p') ? 'm' : 0);
329 
330 	case ESC:
331 	case ctl('g'):	return (ESC);
332 
333 	default:	return (0);
334     }
335     /*NOTREACHED*/
336 }
337 
338 void
339 openrow (rs)
340 int	rs;
341 {
342     register	r, c;
343     struct ent	**tmprow, **pp;
344 
345     if (rs > maxrow) maxrow = rs;
346     if (maxrow >= maxrows - 1 || rs > maxrows - 1) {
347 	if (!growtbl(GROWROW, rs, 0))
348 		return;
349     }
350 	/*
351 	 * save the last active row+1, shift the rows downward, put the last
352 	 * row in place of the first
353 	 */
354     tmprow = tbl[++maxrow];
355     for (r = maxrow; r > rs; r--) {
356 	row_hidden[r] = row_hidden[r-1];
357 	tbl[r] = tbl[r-1];
358 	pp = ATBL(tbl, r, 0);
359 	for (c = 0; c < maxcols; c++, pp++)
360 		if (*pp)
361 			(*pp)->row = r;
362     }
363     tbl[r] = tmprow;	/* the last row was never used.... */
364     FullUpdate++;
365     modflg++;
366 }
367 
368 void
369 closerow (r)
370 register r;
371 {
372     register struct ent **pp;
373     register c;
374     struct ent	**tmprow;
375 
376     if (r > maxrow) return;
377 
378     /* save the row and empty it out */
379     tmprow = tbl[r];
380     pp = ATBL(tbl, r, 0);
381     for (c=maxcol+1; --c>=0; pp++) {
382 	if (*pp)
383 	{	free_ent(*pp);
384 		*pp = (struct ent *)0;
385 	}
386     }
387 
388     /* move the rows, put the deleted row at the end */
389     for (; r < maxrows - 1; r++) {
390 	row_hidden[r] = row_hidden[r+1];
391 	tbl[r] = tbl[r+1];
392 	pp = ATBL(tbl, r, 0);
393 	for (c = 0; c < maxcols; c++, pp++)
394 		if (*pp)
395 			(*pp)->row = r;
396     }
397     tbl[r] = tmprow;
398 
399     maxrow--;
400     FullUpdate++;
401     modflg++;
402 }
403 
404 void
405 opencol (cs, numcol)
406 int	cs;
407 int	numcol;
408 {
409     register r;
410     register struct ent **pp;
411     register c;
412     register lim = maxcol-cs+1;
413     int i;
414 
415     if (cs > maxcol)
416 	maxcol = cs;
417     maxcol += numcol;
418 
419     if ((maxcol >= maxcols - 1) && !growtbl(GROWCOL, 0, maxcol))
420 		return;
421 
422     for (i = maxcol; i > cs; i--) {
423 	fwidth[i] = fwidth[i-numcol];
424 	precision[i] = precision[i-numcol];
425 	col_hidden[i] = col_hidden[i-numcol];
426     }
427     for (c = cs; c - cs < numcol; c++)
428     {	fwidth[c] = DEFWIDTH;
429 	precision[c] =  DEFPREC;
430     }
431 
432     for (r=0; r<=maxrow; r++) {
433 	pp = ATBL(tbl, r, maxcol);
434 	for (c=lim; --c>=0; pp--)
435 	    if (pp[0] = pp[-numcol])
436 		pp[0]->col += numcol;
437 
438 	pp = ATBL(tbl, r, cs);
439 	for (c = cs; c - cs < numcol; c++, pp++)
440 		*pp = (struct ent *)0;
441     }
442     FullUpdate++;
443     modflg++;
444 }
445 
446 void
447 closecol (cs, numcol)
448 int cs;
449 int	numcol;
450 {
451     register r;
452     register struct ent **pp;
453     register struct ent *q;
454     register c;
455     register lim = maxcol-cs;
456     int i;
457     char buf[50];
458 
459     if (lim - numcol < -1)
460     {	sprintf(buf, "Can't delete %d column%s %d columns left", numcol,
461 			(numcol > 1 ? "s," : ","), lim+1);
462 	error(buf);
463 	return;
464     }
465     flush_saved();
466     erase_area(0, curcol, maxrow, curcol + numcol - 1);
467     sync_refs();
468 
469     /* clear then copy the block left */
470     lim = maxcols - numcol - 1;
471     for (r=0; r<=maxrow; r++) {
472 	for (c = cs; c - cs < numcol; c++)
473 		if (q = *ATBL(tbl, r, c))
474 			free_ent(q);
475 
476 	pp = ATBL(tbl, r, cs);
477 	for (c=cs; c <= lim; c++, pp++)
478 	{   if (c > lim)
479 		*pp = (struct ent *)0;
480 	    else
481 	    if (pp[0] = pp[numcol])
482 		pp[0]->col -= numcol;
483 	}
484 
485 	c = numcol;
486 	for (; --c >= 0; pp++)
487 		*pp = (struct ent *)0;
488     }
489 
490     for (i = cs; i < maxcols - numcol - 1; i++) {
491 	fwidth[i] = fwidth[i+numcol];
492 	precision[i] = precision[i+numcol];
493 	col_hidden[i] = col_hidden[i+numcol];
494     }
495     for (; i < maxcols - 1; i++) {
496 	fwidth[i] = DEFWIDTH;
497 	precision[i] = DEFPREC;
498 	col_hidden[i] = 0;
499     }
500 
501     maxcol -= numcol;
502     FullUpdate++;
503     modflg++;
504 }
505 
506 void
507 doend(rowinc, colinc)
508 int rowinc, colinc;
509 {
510     register struct ent *p;
511     int r, c;
512 
513     if (VALID_CELL(p, currow, curcol)) {
514 	r = currow + rowinc;
515 	c = curcol + colinc;
516 	if (r >= 0 && r < maxrows &&
517 	    c >= 0 && c < maxcols &&
518 	    !VALID_CELL(p, r, c)) {
519 		currow = r;
520 		curcol = c;
521 	}
522     }
523 
524     if (!VALID_CELL(p, currow, curcol)) {
525         switch (rowinc) {
526         case -1:
527 	    while (!VALID_CELL(p, currow, curcol) && currow > 0)
528 		currow--;
529 	    break;
530         case  1:
531 	    while (!VALID_CELL(p, currow, curcol) && currow < maxrows-1)
532 		currow++;
533 	    break;
534         case  0:
535             switch (colinc) {
536  	    case -1:
537 	        while (!VALID_CELL(p, currow, curcol) && curcol > 0)
538 		    curcol--;
539 	        break;
540  	    case  1:
541 	        while (!VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
542 		    curcol++;
543 	        break;
544 	    }
545             break;
546         }
547 
548 	error ("");	/* clear line */
549 	return;
550     }
551 
552     switch (rowinc) {
553     case -1:
554 	while (VALID_CELL(p, currow, curcol) && currow > 0)
555 	    currow--;
556 	break;
557     case  1:
558 	while (VALID_CELL(p, currow, curcol) && currow < maxrows-1)
559 	    currow++;
560 	break;
561     case  0:
562 	switch (colinc) {
563 	case -1:
564 	    while (VALID_CELL(p, currow, curcol) && curcol > 0)
565 		curcol--;
566 	    break;
567 	case  1:
568 	    while (VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
569 		curcol++;
570 	    break;
571 	}
572 	break;
573     }
574     if (!VALID_CELL(p, currow, curcol)) {
575         currow -= rowinc;
576         curcol -= colinc;
577     }
578 }
579 
580 void
581 doformat(c1,c2,w,p)
582 int c1,c2,w,p;
583 {
584     register int i;
585 
586     if (w > COLS - RESCOL - 2) {
587 	error("Format too large - Maximum = %d", COLS - RESCOL - 2);
588 	w = COLS-RESCOL-2;
589     }
590 
591     if (p > w) {
592 	error("Precision too large");
593 	p = w;
594     }
595 
596     for(i = c1; i<=c2; i++)
597 	fwidth[i] = w, precision[i] = p;
598 
599     FullUpdate++;
600     modflg++;
601 }
602 
603 void
604 print_options(f)
605 FILE *f;
606 {
607     if(
608        autocalc &&
609        propagation == 10 &&
610        calc_order == BYROWS &&
611        !numeric &&
612        prescale == 1.0 &&
613        !extfunc &&
614        showcell &&
615        showtop &&
616        tbl_style == 0
617       )
618 		return;		/* No reason to do this */
619 
620     (void) fprintf(f, "set");
621     if(!autocalc)
622 	(void) fprintf(f," !autocalc");
623     if(propagation != 10)
624 	(void) fprintf(f, " iterations = %d", propagation);
625     if(calc_order != BYROWS )
626 	(void) fprintf(f, " bycols");
627     if (numeric)
628 	(void) fprintf(f, " numeric");
629     if (prescale != 1.0)
630 	(void) fprintf(f, " prescale");
631     if (extfunc)
632 	(void) fprintf(f, " extfun");
633     if (!showcell)
634 	(void) fprintf(f, " !cellcur");
635     if (!showtop)
636 	(void) fprintf(f, " !toprow");
637     if (tbl_style)
638 	(void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" :
639 					tbl_style == LATEX ? "latex" :
640 					tbl_style == TEX ? "tex" : "0" );
641     (void) fprintf(f, "\n");
642 }
643 
644 void
645 printfile (fname, r0, c0, rn, cn)
646 char *fname;
647 int r0, c0, rn, cn;
648 {
649     FILE *f;
650     char pline[FBUFLEN];
651     int plinelim;
652     int pid;
653     int fieldlen, nextcol;
654     register row, col;
655     register struct ent **pp;
656 
657     if ((strcmp(fname, curfile) == 0) &&
658 	!yn_ask("Confirm that you want to destroy the data base: (y,n)"))
659 	return;
660 
661     if ((f = openout(fname, &pid)) == (FILE *)0)
662     {	error ("Can't create file \"%s\"", fname);
663 	return;
664     }
665     for (row=r0;row<=rn; row++) {
666 	register c = 0;
667 
668 	if (row_hidden[row])
669 	    continue;
670 
671 	pline[plinelim=0] = '\0';
672 	for (pp = ATBL(tbl, row, col=c0); col<=cn;
673 	        pp += nextcol-col, col = nextcol, c += fieldlen) {
674 
675 	    nextcol = col+1;
676 	    if (col_hidden[col]) {
677 		fieldlen = 0;
678 		continue;
679 	    }
680 
681 	    fieldlen = fwidth[col];
682 	    if (*pp) {
683 		char *s;
684 
685 		while (plinelim<c) pline[plinelim++] = ' ';
686 		plinelim = c;
687 		if ((*pp)->flags&is_valid) {
688 		    (void)sprintf (pline+plinelim,"%*.*f",fwidth[col],
689 		                                precision[col], (*pp)->v);
690 		    plinelim += strlen (pline+plinelim);
691 		}
692 		if (s = (*pp)->label) {
693 		    int slen;
694 		    char *start, *last;
695 		    register char *fp;
696 		    struct ent *nc;
697 
698 		    /* Figure out if the label slops over to a blank field */
699 		    slen = strlen(s);
700 		    while (slen > fieldlen && nextcol <= cn &&
701 			    !((nc = lookat(row,nextcol))->flags & is_valid) &&
702 			    !(nc->label)) {
703 
704 	                if (!col_hidden[nextcol])
705 		 	    fieldlen += fwidth[nextcol];
706 
707 			nextcol++;
708 		    }
709 		    if (slen > fieldlen)
710 			slen = fieldlen;
711 
712 		    /* Now justify and print */
713 		    start = (*pp)->flags & is_leftflush ? pline + c
714 					: pline + c + fieldlen - slen;
715 		    last = pline + c + fieldlen;
716 		    fp = plinelim < c ? pline + plinelim : pline + c;
717 		    while (fp < start)
718 			*fp++ = ' ';
719 		    while (slen--)
720 			*fp++ = *s++;
721 		    if (!((*pp)->flags & is_valid) || fieldlen != fwidth[col])
722 			while(fp < last)
723 			    *fp++ = ' ';
724 		    if (plinelim < fp - pline)
725 			plinelim = fp - pline;
726 		}
727 	    }
728 	}
729 	pline[plinelim++] = '\n';
730 	pline[plinelim] = '\0';
731 	(void) fputs (pline, f);
732     }
733 
734     closeout(f, pid);
735 }
736 
737 void
738 tblprintfile (fname, r0, c0, rn, cn)
739 char *fname;
740 int r0, c0, rn, cn;
741 {
742     FILE *f;
743     int pid;
744     register row, col;
745     register struct ent **pp;
746     char coldelim = DEFCOLDELIM;
747 
748     if ((strcmp(fname, curfile) == 0) &&
749 	!yn_ask("Confirm that you want to destroy the data base: (y,n)"))
750 	    return;
751 
752     if ((f = openout(fname, &pid)) == (FILE *)0)
753     {	error ("Can't create file \"%s\"", fname);
754 	return;
755     }
756 
757     if ( tbl_style == TBL ) {
758 	fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname);
759 	fprintf(f,"tab(%c);\n",coldelim);
760 	for (col=c0;col<=cn; col++) fprintf(f," n");
761 	fprintf(f, ".\n");
762 	}
763     else if ( tbl_style == LATEX ) {
764 	fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname);
765 	for (col=c0;col<=cn; col++) fprintf(f,"c");
766 	fprintf(f, "}\n");
767 	coldelim = '&';
768 	}
769     else if ( tbl_style == TEX ) {
770 	fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n",
771 		progname, cn-c0+1);
772 	coldelim = '&';
773 	}
774 
775     for (row=r0; row<=rn; row++) {
776 	if ( tbl_style == TEX )
777 	    (void) fprintf (f, "\\+");
778 
779 	for (pp = ATBL(tbl, row, col=c0); col<=cn; col++, pp++) {
780 	    if (*pp) {
781 		char *s;
782 		if ((*pp)->flags&is_valid) {
783 		    (void) fprintf (f,"%.*f",precision[col],
784 				(*pp)->v);
785 		}
786 		if (s = (*pp)->label) {
787 	            (void) fprintf (f,"%s",s);
788 		}
789 	    }
790 	    if ( col < cn )
791 		(void) fprintf(f,"%c",coldelim);
792 	}
793 	if ( tbl_style == LATEX ) {
794 	    if ( row < rn ) (void) fprintf (f, "\\\\");
795 	    }
796 	else if ( tbl_style == TEX ) {
797 	    (void) fprintf (f, "\\cr");
798 	    }
799 	(void) fprintf (f,"\n");
800     }
801 
802     if ( tbl_style == TBL )
803     (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname);
804     else if ( tbl_style == LATEX )
805     (void) fprintf (f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname);
806     else if ( tbl_style == TEX )
807     (void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname);
808 
809     closeout(f, pid);
810 }
811 
812 struct enode *
813 copye (e, Rdelta, Cdelta)
814 register struct enode *e;
815 int Rdelta, Cdelta;
816 {
817     register struct enode *ret;
818 
819     if (e == (struct enode *)0) {
820         ret = (struct enode *)0;
821     } else if (e->op & REDUCE) {
822 	int newrow, newcol;
823 	ret = (struct enode *) xmalloc ((unsigned) sizeof (struct enode));
824 	ret->op = e->op;
825 	newrow=e->e.r.left.vf & FIX_ROW ? e->e.r.left.vp->row :
826 					  e->e.r.left.vp->row+Rdelta;
827 	newcol=e->e.r.left.vf & FIX_COL ? e->e.r.left.vp->col :
828 					  e->e.r.left.vp->col+Cdelta;
829 	ret->e.r.left.vp = lookat (newrow, newcol);
830 	ret->e.r.left.vf = e->e.r.left.vf;
831 	newrow=e->e.r.right.vf & FIX_ROW ? e->e.r.right.vp->row :
832 					   e->e.r.right.vp->row+Rdelta;
833 	newcol=e->e.r.right.vf & FIX_COL ? e->e.r.right.vp->col :
834 					   e->e.r.right.vp->col+Cdelta;
835 	ret->e.r.right.vp = lookat (newrow, newcol);
836 	ret->e.r.right.vf = e->e.r.right.vf;
837     } else {
838 	ret = (struct enode *) xmalloc ((unsigned) sizeof (struct enode));
839 	ret->op = e->op;
840 	switch (ret->op) {
841 	case 'v':
842 		{
843 		    int newrow, newcol;
844 		    newrow=e->e.v.vf & FIX_ROW ? e->e.v.vp->row :
845 						 e->e.v.vp->row+Rdelta;
846 		    newcol=e->e.v.vf & FIX_COL ? e->e.v.vp->col :
847 						 e->e.v.vp->col+Cdelta;
848 		    ret->e.v.vp = lookat (newrow, newcol);
849 		    ret->e.v.vf = e->e.v.vf;
850 		    break;
851 		}
852 	case 'k':
853 		ret->e.k = e->e.k;
854 		break;
855 	case 'f':
856 		ret->e.o.right = copye (e->e.o.right,0,0);
857 		ret->e.o.left = (struct enode *)0;
858  		break;
859 	case '$':
860 		ret->e.s = xmalloc((unsigned) strlen(e->e.s)+1);
861 		(void) strcpy(ret->e.s, e->e.s);
862 		break;
863 	default:
864 		ret->e.o.right = copye (e->e.o.right,Rdelta,Cdelta);
865 		ret->e.o.left = copye (e->e.o.left,Rdelta,Cdelta);
866 		break;
867 	}
868     }
869     return ret;
870 }
871 
872 /*
873  * sync_refs and syncref are used to remove references to
874  * deleted struct ents.  Note that the deleted structure must still
875  * be hanging around before the call, but not referenced by an entry
876  * in tbl.  Thus the free_ent, fix_ent calls in sc.c
877  */
878 void
879 sync_refs ()
880 {
881     register i,j;
882     register struct ent *p;
883     sync_ranges();
884     for (i=0; i<=maxrow; i++)
885 	for (j=0; j<=maxcol; j++)
886 	    if ((p = *ATBL(tbl, i, j)) && p->expr)
887 		syncref(p->expr);
888 }
889 
890 void
891 syncref(e)
892 register struct enode *e;
893 {
894     if (e == (struct enode *)0)
895 	return;
896     else if (e->op & REDUCE) {
897  	e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col);
898  	e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col);
899     } else {
900 	switch (e->op) {
901 	case 'v':
902 		e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col);
903 		break;
904 	case 'k':
905 		break;
906 	case '$':
907 		break;
908 	default:
909 		syncref(e->e.o.right);
910 		syncref(e->e.o.left);
911 		break;
912 	}
913     }
914 }
915 
916 void
917 hiderow(arg)
918 int arg;
919 {
920     register int r1;
921     register int r2;
922 
923     r1 = currow;
924     r2 = r1 + arg - 1;
925     if (r1 < 0 || r1 > r2) {
926 	error ("Invalid range");
927 	return;
928     }
929     if (r2 >= maxrows-1)
930     {	if (!growtbl(GROWROW, arg+1, 0))
931 	{	error("You can't hide the last row");
932 		return;
933 	}
934     }
935     FullUpdate++;
936     modflg++;
937     while (r1 <= r2)
938 	row_hidden[r1++] = 1;
939 }
940 
941 void
942 hidecol(arg)
943 int arg;
944 {
945     register int c1;
946     register int c2;
947 
948     c1 = curcol;
949     c2 = c1 + arg - 1;
950     if (c1 < 0 || c1 > c2) {
951 	error ("Invalid range");
952 	return;
953     }
954     if (c2 >= maxcols-1)
955     {	if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1))
956 	{	error("You can't hide the last col");
957 		return;
958 	}
959     }
960     FullUpdate++;
961     modflg++;
962     while (c1 <= c2)
963 	col_hidden[c1++] = 1;
964 }
965 
966 void
967 showrow(r1, r2)
968 int r1, r2;
969 {
970     if (r1 < 0 || r1 > r2) {
971 	error ("Invalid range");
972 	return;
973     }
974     if (r2 > maxrows-1) {
975 	r2 = maxrows-1;
976     }
977     FullUpdate++;
978     modflg++;
979     while (r1 <= r2)
980 	row_hidden[r1++] = 0;
981 }
982 
983 void
984 showcol(c1, c2)
985 int c1, c2;
986 {
987     if (c1 < 0 || c1 > c2) {
988 	error ("Invalid range");
989 	return;
990     }
991     if (c2 > maxcols-1) {
992 	c2 = maxcols-1;
993     }
994     FullUpdate++;
995     modflg++;
996     while (c1 <= c2)
997 	col_hidden[c1++] = 0;
998 }
999 
1000 /* Open the output file, setting up a pipe if needed */
1001 
1002 FILE *
1003 openout(fname, rpid)
1004 char *fname;
1005 int *rpid;
1006 {
1007     int pipefd[2];
1008     int pid;
1009     FILE *f;
1010     char *efname;
1011 
1012     while (*fname && (*fname == ' '))  /* Skip leading blanks */
1013 	fname++;
1014 
1015     if (*fname != '|') {		/* Open file if not pipe */
1016 	*rpid = 0;
1017 
1018 	efname = findhome(fname);
1019 #ifdef DOBACKUPS
1020 	if (!backup_file(efname) &&
1021 	    (yn_ask("Could not create backup copy, Save anyhow?: (y,n)") != 1))
1022 		return(0);
1023 #endif
1024 	return(fopen(efname, "w"));
1025     }
1026 
1027     fname++;				/* Skip | */
1028     if ( pipe (pipefd) < 0) {
1029 	error("Can't make pipe to child");
1030 	*rpid = 0;
1031 	return(0);
1032     }
1033 
1034     deraw();
1035 #ifdef VMS
1036     fprintf(stderr, "No son tasks available yet under VMS--sorry\n");
1037 #else /* VMS */
1038 
1039     if ((pid=fork()) == 0)			  /* if child  */
1040     {
1041 	(void) close (0);			  /* close stdin */
1042 	(void) close (pipefd[1]);
1043 	(void) dup (pipefd[0]);		  /* connect to pipe input */
1044 	(void) signal (SIGINT, SIG_DFL);	  /* reset */
1045 	(void) execl ("/bin/sh", "sh", "-c", fname, 0);
1046 	exit (-127);
1047     }
1048     else				  /* else parent */
1049     {
1050 	*rpid = pid;
1051 	if ((f = fdopen (pipefd[1], "w")) == (FILE *)0)
1052 	{
1053 	    (void) kill (pid, -9);
1054 	    error ("Can't fdopen output");
1055 	    (void) close (pipefd[1]);
1056 	    *rpid = 0;
1057 	    return(0);
1058 	}
1059     }
1060 #endif /* VMS */
1061     return(f);
1062 }
1063 
1064 void
1065 closeout(f, pid)
1066 FILE *f;
1067 int pid;
1068 {
1069     int temp;
1070 
1071     (void) fclose (f);
1072     if (pid) {
1073          while (pid != wait(&temp)) /**/;
1074 	 (void) printf("Press RETURN to continue ");
1075 	 (void) fflush(stdout);
1076 	 (void) nmgetch();
1077 	 goraw();
1078     }
1079 }
1080 
1081 void
1082 copyent(n,p,dr,dc)
1083 	    register struct ent *n, *p;
1084 	    int dr, dc;
1085 {
1086     if(!n||!p){error("internal error");return;}
1087     n -> v = p -> v;
1088     n -> flags = p -> flags;
1089     n -> expr = copye (p -> expr, dr, dc);
1090     n -> label = (char *)0;
1091     if (p -> label) {
1092 	n -> label = (char *)
1093 		xmalloc  ((unsigned) (strlen (p -> label) + 1));
1094 	(void) strcpy (n -> label, p -> label);
1095     }
1096 }
1097 
1098 void
1099 write_fd (f, r0, c0, rn, cn)
1100 register FILE *f;
1101 int r0, c0, rn, cn;
1102 {
1103     register struct ent **pp;
1104     register r, c;
1105 
1106     (void) fprintf (f, "# This data file was generated by the Spreadsheet ");
1107     (void) fprintf (f, "Calculator.\n");
1108     (void) fprintf (f, "# You almost certainly shouldn't edit it.\n\n");
1109     print_options(f);
1110     for (c=0; c<maxcols; c++)
1111 	if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC)
1112 	    (void) fprintf (f, "format %s %d %d\n",coltoa(c),fwidth[c],precision[c]);
1113     for (c=c0; c<cn; c++) {
1114         if (col_hidden[c]) {
1115             (void) fprintf(f, "hide %s\n", coltoa(c));
1116         }
1117     }
1118     for (r=r0; r<=rn; r++) {
1119 	if (row_hidden[r]) {
1120 	    (void) fprintf(f, "hide %d\n", r);
1121 	}
1122     }
1123 
1124     write_range(f);
1125 
1126     if (mdir)
1127 	    (void) fprintf(f, "mdir \"%s\"\n", mdir);
1128     for (r=r0; r<=rn; r++) {
1129 	pp = ATBL(tbl, r, c0);
1130 	for (c=c0; c<=cn; c++, pp++)
1131 	    if (*pp) {
1132 		if ((*pp)->label) {
1133 		    edits(r,c);
1134 		    (void) fprintf(f, "%s\n",line);
1135 		}
1136 		if ((*pp)->flags&is_valid) {
1137 		    editv (r, c);
1138 		    (void) fprintf (f, "%s\n",line);
1139 		}
1140 	    }
1141     }
1142 }
1143 
1144 int
1145 writefile (fname, r0, c0, rn, cn)
1146 char *fname;
1147 int r0, c0, rn, cn;
1148 {
1149     register FILE *f;
1150     char save[PATHLEN];
1151     int pid;
1152 
1153 #ifndef VMS
1154     if (Crypt) {
1155 	return (cwritefile(fname, r0, c0, rn, cn));
1156     }
1157 #endif /* VMS */
1158 
1159     if (*fname == '\0') fname = curfile;
1160 
1161     (void) strcpy(save,fname);
1162 
1163     if ((f= openout(fname, &pid)) == (FILE *)0)
1164     {	error ("Can't create file \"%s\"", fname);
1165 	return (-1);
1166     }
1167 
1168     write_fd(f, r0, c0, rn, cn);
1169 
1170     closeout(f, pid);
1171 
1172     if (!pid) {
1173         (void) strcpy(curfile, save);
1174         modflg = 0;
1175         error("File \"%s\" written.",curfile);
1176     }
1177 
1178     return (0);
1179 }
1180 
1181 void
1182 readfile (fname,eraseflg)
1183 char *fname;
1184 int eraseflg;
1185 {
1186     register FILE *f;
1187     char save[PATHLEN];
1188 
1189     if (*fname == '*' && mdir) {
1190        (void) strcpy(save, mdir);
1191        *fname = '/';
1192        (void) strcat(save, fname);
1193     } else {
1194         if (*fname == '\0')
1195 	    fname = curfile;
1196         (void) strcpy(save,fname);
1197     }
1198 
1199 #ifndef VMS
1200     if (Crypt)  {
1201 	creadfile(save, eraseflg);
1202 	return;
1203     }
1204 #endif /* VMS */
1205 
1206     if (eraseflg && strcmp(fname,curfile) && modcheck(" first")) return;
1207 
1208     if ((f = fopen(findhome(save), "r")) == (FILE *)0)
1209     {	error ("Can't read file \"%s\"", save);
1210 	return;
1211     }
1212 
1213     if (eraseflg) erasedb ();
1214 
1215     loading++;
1216     while (fgets(line,sizeof line,f)) {
1217 	linelim = 0;
1218 	if (line[0] != '#') (void) yyparse ();
1219     }
1220     --loading;
1221     (void) fclose (f);
1222     linelim = -1;
1223     modflg++;
1224     if (eraseflg) {
1225 	(void) strcpy(curfile,save);
1226 	modflg = 0;
1227     }
1228     EvalAll();
1229 }
1230 
1231 void
1232 erasedb ()
1233 {
1234     register r, c;
1235     for (c = 0; c<=maxcol; c++) {
1236 	fwidth[c] = DEFWIDTH;
1237 	precision[c] = DEFPREC;
1238     }
1239 
1240     for (r = 0; r<=maxrow; r++) {
1241 	register struct ent **pp = ATBL(tbl, r, 0);
1242 	for (c=0; c++<=maxcol; pp++)
1243 	    if (*pp) {
1244 		if ((*pp)->expr) efree (*pp, (*pp) -> expr);
1245 		if ((*pp)->label) xfree ((char *)((*pp) -> label));
1246 		xfree ((char *)(*pp));
1247 		*pp = (struct ent *)0;
1248 	    }
1249     }
1250     maxrow = 0;
1251     maxcol = 0;
1252     clean_range();
1253     FullUpdate++;
1254 }
1255 
1256 void
1257 backcol(arg)
1258 	int arg;
1259 {
1260     while (--arg>=0) {
1261 	if (curcol)
1262 	    curcol--;
1263 	else
1264 	    {error ("At column A"); break;}
1265 	while(col_hidden[curcol] && curcol)
1266 	    curcol--;
1267     }
1268 }
1269 
1270 void
1271 forwcol(arg)
1272 	int arg;
1273 {
1274     while (--arg>=0) {
1275 	if (curcol < maxcols - 1)
1276 	    curcol++;
1277 	else
1278 	if (!growtbl(GROWCOL, 0, arg))	/* get as much as needed */
1279 		break;
1280 	while(col_hidden[curcol]&&(curcol<maxcols-1))
1281 	    curcol++;
1282     }
1283 }
1284 
1285 void
1286 forwrow(arg)
1287 	int arg;
1288 {
1289     while (--arg>=0) {
1290 	if (currow < maxrows - 1)
1291 	    currow++;
1292 	else
1293 	if (!growtbl(GROWROW, arg, 0))	/* get as much as needed */
1294 		break;
1295 	while (row_hidden[currow]&&(currow<maxrows-1))
1296 	    currow++;
1297     }
1298 }
1299 
1300 void
1301 backrow(arg)
1302 	int arg;
1303 {
1304     while (--arg>=0) {
1305 	if (currow)
1306 	    currow--;
1307 	else
1308 	    {error ("At row zero"); break;}
1309 	while (row_hidden[currow] && currow)
1310 	    currow--;
1311     }
1312 }
1313 
1314 
1315 /*
1316  * Show a cell's label string or expression value.  May overwrite value if
1317  * there is one already displayed in the cell.  Created from old code in
1318  * update(), copied with minimal changes.
1319  */
1320 
1321 void
1322 showstring (string, leftflush, hasvalue, row, col, nextcolp, mxcol, fieldlenp, r, c)
1323     char *string;	/* to display */
1324     int leftflush;	/* or rightflush */
1325     int hasvalue;	/* is there a numeric value? */
1326     int row, col;	/* spreadsheet location */
1327     int *nextcolp;	/* value returned through it */
1328     int mxcol;		/* last column displayed? */
1329     int *fieldlenp;	/* value returned through it */
1330     int r, c;		/* screen row and column */
1331 {
1332     register int nextcol  = *nextcolp;
1333     register int fieldlen = *fieldlenp;
1334 
1335     char field[FBUFLEN];
1336     int  slen;
1337     char *start, *last;
1338     register char *fp;
1339     struct ent *nc;
1340 
1341     /* This figures out if the label is allowed to
1342        slop over into the next blank field */
1343 
1344     slen = strlen (string);
1345     while ((slen > fieldlen) && (nextcol <= mxcol) &&
1346 	   !((nc = lookat (row, nextcol)) -> flags & is_valid) &&
1347 	   !(nc->label)) {
1348 
1349 	if (! col_hidden [nextcol])
1350 	    fieldlen += fwidth [nextcol];
1351 
1352 	nextcol++;
1353     }
1354     if (slen > fieldlen)
1355 	slen = fieldlen;
1356 
1357     /* Now justify and print */
1358     start = leftflush ? field : field + fieldlen - slen;
1359     last = field+fieldlen;
1360     fp = field;
1361     while (fp < start)
1362 	*fp++ = ' ';
1363     while (slen--)
1364 	*fp++ = *string++;
1365     if ((! hasvalue) || fieldlen != fwidth[col])
1366 	while (fp < last)
1367 	    *fp++ = ' ';
1368     *fp = '\0';
1369 #ifdef VMS
1370     mvaddstr(r, c, field);	/* this is a macro */
1371 #else
1372     (void) mvaddstr(r, c, field);
1373 #endif
1374 
1375     *nextcolp  = nextcol;
1376     *fieldlenp = fieldlen;
1377 }
1378 
1379 int
1380 etype(e)
1381 register struct enode *e;
1382 {
1383     if (e == (struct enode *)0)
1384 	return NUM;
1385     switch (e->op) {
1386     case O_SCONST: case '#': case DATE: case FMT: case STINDEX:
1387     case EXT: case SVAL: case SUBSTR:
1388         return (STR);
1389 
1390     case '?':
1391     case IF:
1392         return(etype(e->e.o.right->e.o.left));
1393 
1394     case 'f':
1395         return(etype(e->e.o.right));
1396 
1397     case O_VAR: {
1398 	register struct ent *p;
1399 	p = e->e.v.vp;
1400 	if (p->expr)
1401 	    return(p->flags & is_strexpr ? STR : NUM);
1402 	else if (p->label)
1403 	    return(STR);
1404 	else
1405 	    return(NUM);
1406 	}
1407 
1408     default:
1409 	return(NUM);
1410     }
1411 }
1412 
1413 /* return 1 if yes given, 0 otherwise */
1414 int
1415 yn_ask(msg)
1416 char	*msg;
1417 {	char ch;
1418 
1419 	(void) move (0, 0);
1420 	(void) clrtoeol ();
1421 	(void) addstr (msg);
1422 	(void) refresh();
1423 	ch = nmgetch();
1424 	if ( ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N' ) {
1425 		if (ch == ctl('g') || ch == ESC)
1426 			return(-1);
1427 		error("y or n response required");
1428 		return (-1);
1429 	}
1430 	if (ch == 'y' || ch == 'Y')
1431 		return(1);
1432 	else
1433 		return(0);
1434 }
1435 
1436 #include <pwd.h>
1437 char	*
1438 findhome(path)
1439 char	*path;
1440 {
1441 	static	char	*HomeDir = NULL;
1442 	extern	char	*getenv();
1443 
1444 	if (*path == '~')
1445 	{	char	*pathptr;
1446 		char	tmppath[PATHLEN];
1447 
1448 		if (HomeDir == NULL)
1449 		{	HomeDir = getenv("HOME");
1450 			if (HomeDir == NULL)
1451 				HomeDir = "/";
1452 		}
1453 		pathptr = path + 1;
1454 		if ((*pathptr == '/') || (*pathptr == '\0'))
1455 		{	strcpy(tmppath, HomeDir);
1456 		}
1457 		else
1458 		{	struct	passwd *pwent;
1459 			extern	struct	passwd *getpwnam();
1460 			char	*namep;
1461 			char	name[50];
1462 
1463 			namep = name;
1464 			while ((*pathptr != '\0') && (*pathptr != '/'))
1465 				*(namep++) = *(pathptr++);
1466 			*namep = '\0';
1467 			if ((pwent = getpwnam(name)) == NULL)
1468 			{	sprintf(path, "Can't find user %s", name);
1469 				return(NULL);
1470 			}
1471 			strcpy(tmppath, pwent->pw_dir);
1472 		}
1473 
1474 		strcat(tmppath, pathptr);
1475 		strcpy(path, tmppath);
1476 	}
1477 	return(path);
1478 }
1479 
1480 #ifdef DOBACKUPS
1481 #include <sys/types.h>
1482 #include <sys/stat.h>
1483 
1484 /*
1485  * make a backup copy of a file, use the same mode and name in the format
1486  * [path/]#file~
1487  * return 1 if we were successful, 0 otherwise
1488  */
1489 int
1490 backup_file(path)
1491 char	*path;
1492 {
1493 	struct	stat	statbuf;
1494 	char	fname[PATHLEN];
1495 	char	tpath[PATHLEN];
1496 #ifdef sequent
1497 	char	*buf;
1498 #else
1499 	char	buf[BUFSIZ];
1500 #endif
1501 	char	*tpp;
1502 	int	infd, outfd;
1503 	int	count;
1504 
1505 	/* tpath will be the [path/]file ---> [path/]#file~ */
1506 	strcpy(tpath, path);
1507 	if ((tpp = strrchr(tpath, '/')) == NULL)
1508 		tpp = tpath;
1509 	else
1510 		tpp++;
1511 	strcpy(fname, tpp);
1512 	sprintf(tpp, "#%s~", fname);
1513 
1514 	if (stat(path, &statbuf) == 0)
1515 	{
1516 #ifdef sequent
1517 		if ((buf = xmalloc(statbuf.st_blksize)) == (char *)0)
1518 			return(0);
1519 #endif
1520 
1521 		if ((infd = open(path, O_RDONLY, 0)) < 0)
1522 		{
1523 #ifdef sequent
1524 			xfree(buf);
1525 #endif
1526 			return(0);
1527 		}
1528 		if ((outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT,
1529 					statbuf.st_mode)) < 0)
1530 		{
1531 #ifdef sequent
1532 			xfree(buf);
1533 #endif
1534 			return(0);
1535 		}
1536 #ifdef sequent
1537 		while((count = read(infd, buf, statbuf.st_blksize)) > 0)
1538 #else
1539 		while((count = read(infd, buf, sizeof(buf))) > 0)
1540 #endif
1541 		{	if (write(outfd, buf, count) != count)
1542 			{	count = -1;
1543 				break;
1544 			}
1545 		}
1546 		close(infd);
1547 		close(outfd);
1548 #ifdef sequent
1549 		xfree(buf);
1550 #endif
1551 		return((count < 0) ? 0 : 1);
1552 	}
1553 	else
1554 	if (errno == ENOENT)
1555 		return(1);
1556 	return(0);
1557 }
1558 #endif
1559