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: 1.1 $
11  *
12  *		More mods by Dan Coppersmith, 5/94
13  */
14 #include <config.h>
15 
16 #include <sys/types.h>
17 #include <curses.h>
18 
19 #ifdef HAVE_X11_X_H
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #endif /* HAVE_X11_X_H */
23 
24 #ifdef SYSV
25 #include <fcntl.h>
26 #else
27 #if defined(BSD42) || defined(BSD43) || defined(VMS)
28 #include <sys/file.h>
29 #else
30 #include <fcntl.h>
31 #endif
32 #endif
33 
34 #include "sc.h"
35 
36 #ifdef HAVE_X11_X_H
37 #include "scXstuff.h"
38 #endif /* HAVE_X11_X_H */
39 #include <signal.h>
40 #include <errno.h>
41 
42 static	void	openrow PROTO((int));
43 static	void	print_options PROTO((FILE *));
44 static	void	syncref PROTO((struct enode *));
45 static	void	unspecial PROTO((FILE *, char *, int));
46 
47 static char	*file_mode = "w";	/* write/append */
48 extern	int	errno;
49 
50 /* To store location of previous cursor postion */
51 extern  int     prvmx, prvmy, prvcol;     /* line added by Bob Parbs 12-92 */
52 extern  int     lastmx, lastmy, lastcol;  /* line added by Bob Parbs 12-92 */
53 
54 #define DEFCOLDELIM ':'
55 
56 /* copy the current row (currow) and place the cursor in the new row */
57 void
duprow()58 duprow()
59 {
60     if (currow >= maxrows - 1 || maxrow >= maxrows - 1) {
61 	if (!growtbl(GROWROW, 0, 0))
62 		return;
63     }
64     modflg++;
65     currow++;
66     openrow (currow);
67     for (curcol = 0; curcol <= maxcol; curcol++) {
68 	register struct ent *p = *ATBL(tbl, currow - 1, curcol);
69 	if (p) {
70 	    register struct ent *n;
71 	    n = lookat (currow, curcol);
72 	    (void)copyent ( n, p, 1, 0);
73 	}
74     }
75     for (curcol = 0; curcol <= maxcol; curcol++) {
76 	register struct ent *p = *ATBL(tbl, currow, curcol);
77 	if (p && (p -> flags & is_valid) && !p -> expr)
78 	    break;
79     }
80     if (curcol > maxcol)
81 	curcol = 0;
82 }
83 
84 /* copy the current column (curcol) and place the cursor in the new column */
85 void
dupcol()86 dupcol()
87 {
88     if (curcol >= maxcols - 1 || maxcol >= maxcols - 1) {
89 	if (!growtbl(GROWCOL, 0, 0))
90 		return;
91     }
92     modflg++;
93     curcol++;
94     opencol (curcol, 1);
95     for (currow = 0; currow <= maxrow; currow++) {
96 	register struct ent *p = *ATBL(tbl, currow, curcol - 1);
97 	if (p) {
98 	    register struct ent *n;
99 	    n = lookat (currow, curcol);
100 	    copyent ( n, p, 0, 1);
101 	}
102     }
103     for (currow = 0; currow <= maxrow; currow++) {
104 	register struct ent *p = *ATBL(tbl, currow, curcol);
105 	if (p && (p -> flags & is_valid) && !p -> expr)
106 	    break;
107     }
108     if (currow > maxrow)
109 	currow = 0;
110 }
111 
112 /* insert 'arg' rows before currow */
113 void
insertrow(arg)114 insertrow(arg)
115 register int arg;
116 {
117     while (--arg>=0) openrow (currow);
118 }
119 
120 /* delete 'arg' rows starting at currow (deletes from currow downward) */
121 void
deleterow(arg)122 deleterow(arg)
123 register int arg;
124 {
125     if (any_locked_cells(currow, 0, currow + arg - 1, maxcol))
126 	scerror("Locked cells encountered. Nothing changed");
127     else {
128     	flush_saved();
129     	erase_area(currow, 0, currow + arg - 1, maxcol);
130     	currow += arg;
131     	while (--arg>=0) closerow (--currow);
132     	sync_refs();
133     }
134 }
135 
136 #ifdef NEW
137 void
erase_area(sr,sc,er,ec)138 erase_area(sr, sc, er, ec)
139 int sr, sc, er, ec;
140 {	erase_and_pull(sr, sc, er, ec, 1, 1);
141 }
142 
143 void
erase_and_pull(sr,sc,er,ec,eraseflg,pull)144 erase_and_pull(sr, sc, er, ec, eraseflg, pull)
145 int sr, sc, er, ec;
146 int	eraseflg;
147 int	pull;
148 {
149     register int r, c;
150     register struct ent **pp;
151 
152     if (sr > er) {
153 	r = sr; sr = er; er= r;
154     }
155 
156     if (sc > ec) {
157 	c = sc; sc = ec; ec= c;
158     }
159 
160     if (sr < 0)
161 	sr = 0;
162     if (sc < 0)
163 	sc = 0;
164     checkbounds(&er, &ec);
165 
166     for (r = sr; r <= er; r++) {
167 	for (c = sc; c <= ec; c++) {
168 	    pp = ATBL(tbl, r, c);
169 /*	    if (*pp && !((*pp)->flags&is_locked)) {
170 FIXME		free_ent(*pp);
171 		*pp = (struct ent *)0;
172 	    }
173 */
174 	    if (*pp)
175 	    {
176 		struct ent	*p = *pp;
177 
178 		if (eraseflg && !((*pp)->flags&is_locked)) {
179 			*pp = (struct ent *)0;
180 			if (!pull)
181 				del_ent(p);	/* untested */
182 		}
183 		if (pull)
184 		{
185 			/* if am JUST pulling, need to make a copy */
186 			if (!eraseflg) {
187 				*pp = new_ent();
188 				copyent(*pp, p, 0, 0);
189 				(*pp)->row = p->row;
190 				(*pp)->col = p->col;
191 			}
192 
193 			free_ent(p);
194 		}
195 	    }
196 	}
197     }
198 }
199 #endif
200 void
erase_area(sr,sc,er,ec)201 erase_area(sr, sc, er, ec)
202 int sr, sc, er, ec;
203 {
204     register int r, c;
205     register struct ent **pp;
206 
207     if (sr > er) {
208 	r = sr; sr = er; er= r;
209     }
210 
211     if (sc > ec) {
212 	c = sc; sc = ec; ec= c;
213     }
214 
215     if (sr < 0)
216 	sr = 0;
217     if (sc < 0)
218 	sc = 0;
219     checkbounds(&er, &ec);
220 
221     for (r = sr; r <= er; r++) {
222 	for (c = sc; c <= ec; c++) {
223 	    pp = ATBL(tbl, r, c);
224 	    if (*pp && !((*pp)->flags&is_locked)) {
225 		free_ent(*pp);
226 		*pp = (struct ent *)0;
227 	    }
228 	}
229     }
230 }
231 
232 /*
233  * deletes the expression associated w/ a cell and turns it into a constant
234  * containing whatever was on the screen
235  */
236 void
valueize_area(sr,sc,er,ec)237 valueize_area(sr, sc, er, ec)
238 int sr, sc, er, ec;
239 {
240     register int r, c;
241     register struct ent *p;
242 
243     if (sr > er) {
244 	r = sr; sr = er; er= r;
245     }
246 
247     if (sc > ec) {
248 	c = sc; sc = ec; ec= c;
249     }
250 
251     if (sr < 0)
252 	sr = 0;
253     if (sc < 0)
254 	sc = 0;
255     checkbounds(&er, &ec);
256 
257     for (r = sr; r <= er; r++) {
258 	for (c = sc; c <= ec; c++) {
259 	    p = *ATBL(tbl, r, c);
260 	    if (p && p->flags&is_locked) {
261 		sprintf(stringbuf, " Cell %s%d is locked", coltoa(c), r);
262 		scerror(stringbuf);
263 		continue;
264 	    }
265 	    if (p && p->expr) {
266 		efree(p->expr);
267 		p->expr = (struct enode *)0;
268 		p->flags &= ~is_strexpr;
269 	    }
270 	}
271     }
272 
273 }
274 
275 void
pullcells(to_insert)276 pullcells(to_insert)
277 int to_insert;
278 {
279     register struct ent *p, *n;
280     register int deltar, deltac;
281     int minrow, mincol;
282     int mxrow, mxcol;
283     int numrows, numcols;
284 
285     if (! to_fix)
286     {
287 	scerror ("No data to pull");
288 	return;
289     }
290 
291     minrow = maxrows;
292     mincol = maxcols;
293     mxrow = 0;
294     mxcol = 0;
295 
296     for (p = to_fix; p; p = p->next) {
297 	if (p->row < minrow)
298 	    minrow = p->row;
299 	if (p->row > mxrow)
300 	    mxrow = p->row;
301 	if (p->col < mincol)
302 	    mincol = p->col;
303 	if (p->col > mxcol)
304 	    mxcol = p->col;
305     }
306 
307     numrows = mxrow - minrow + 1;
308     numcols = mxcol - mincol + 1;
309     deltar = currow - minrow;
310     deltac = curcol - mincol;
311 
312     if (to_insert == 'r') {
313 	insertrow(numrows);
314 	deltac = 0;
315     } else if (to_insert == 'c') {
316 	opencol(curcol, numcols);
317 	deltar = 0;
318     }
319 
320     FullUpdate++;
321     modflg++;
322 
323     for (p = to_fix; p; p = p->next) {
324 	n = lookat (p->row + deltar, p->col + deltac);
325 	(void) clearent(n);
326 	copyent( n, p, deltar, deltac);
327 	n -> flags = p -> flags & ~is_deleted;
328     }
329 }
330 
331 void
colshow_op()332 colshow_op()
333 {
334     register int i,j;
335     for (i=0; i<maxcols; i++)
336 	if (col_hidden[i])
337 	    break;
338     for(j=i; j<maxcols; j++)
339 	if (!col_hidden[j])
340 	    break;
341     j--;
342     if (i>=maxcols)
343 	scerror ("No hidden columns to show");
344     else {
345 	(void) sprintf(line,"show %s:", coltoa(i));
346 	(void) sprintf(line + strlen(line),"%s",coltoa(j));
347 	linelim = strlen (line);
348     }
349 }
350 
351 void
rowshow_op()352 rowshow_op()
353 {
354     register int i,j;
355     for (i=0; i<maxrows; i++)
356 	if (row_hidden[i])
357 	    break;
358     for(j=i; j<maxrows; j++)
359 	if (!row_hidden[j]) {
360 	    break;
361 	}
362     j--;
363 
364     if (i>=maxrows)
365 	scerror ("No hidden rows to show");
366     else {
367 	(void) sprintf(line,"show %d:%d", i, j);
368         linelim = strlen (line);
369     }
370 }
371 
372 #ifdef notdef
373 /*
374  * Given a row/column command letter, emit a small menu, then read a qualifier
375  * character for a row/column command and convert it to 'r' (row), 'c'
376  * (column), or 0 (unknown).  If ch is 'p', an extra qualifier 'm' is allowed.
377  */
378 int
get_rcqual(ch)379 get_rcqual (ch)
380     int ch;
381 {
382 	sprintf(stringbuf, "%sow/column:  r: row  c: column%s",
383 
384 	    (ch == 'i') ? "Insert r" :
385 	    (ch == 'a') ? "Append r" :
386 	    (ch == 'd') ? "Delete r" :
387 	    (ch == 'p') ? "Pull r" :
388 	    (ch == 'v') ? "Values r" :
389 	    (ch == 'z') ? "Zap r" :
390 	    (ch == 's') ? "Show r" : "R",
391 
392 	    (ch == 'p') ? "  m: merge" : "");
393 	scerror(stringbuf);
394 
395 	if (!using_X)
396 	    (void) refresh();
397 
398 	switch (nmgetch())
399 	{
400 		case 'r':
401 		case 'l':
402 		case 'h':
403 		case ctl('f'):
404 		case ctl('b'):	return ('r');
405 
406 		case 'c':
407 		case 'j':
408 		case 'k':
409 		case ctl('p'):
410 		case ctl('n'):	return ('c');
411 
412 		case 'm':	return ((ch == 'p') ? 'm' : 0);
413 
414 		case ESC:
415 		case ctl('g'):	return (ESC);
416 
417 		default:	return (0);
418 	}
419 	/*NOTREACHED*/
420 }
421 #endif
422 
423 static	void
openrow(rs)424 openrow (rs)
425 int	rs;
426 {
427     register	r, c;
428     struct ent	**tmprow, **pp;
429 
430     if (rs > maxrow) maxrow = rs;
431     if (maxrow >= maxrows - 1 || rs > maxrows - 1) {
432 	if (!growtbl(GROWROW, rs, 0))
433 		return;
434     }
435 	/*
436 	 * save the last active row+1, shift the rows downward, put the last
437 	 * row in place of the first
438 	 */
439     tmprow = tbl[++maxrow];
440     for (r = maxrow; r > rs; r--) {
441 	row_hidden[r] = row_hidden[r-1];
442 	tbl[r] = tbl[r-1];
443 	pp = ATBL(tbl, r, 0);
444 	for (c = 0; c < maxcols; c++, pp++)
445 		if (*pp)
446 			(*pp)->row = r;
447     }
448     tbl[r] = tmprow;	/* the last row was never used.... */
449     FullUpdate++;
450     modflg++;
451 }
452 
453 /* delete row r */
454 void
closerow(r)455 closerow (r)
456 register int	r;
457 {
458     register struct ent **pp;
459     register c;
460     struct ent	**tmprow;
461 
462     if (r > maxrow) return;
463 
464     /* save the row and empty it out */
465     tmprow = tbl[r];
466     pp = ATBL(tbl, r, 0);
467     for (c=maxcol+1; --c>=0; pp++) {
468 	if (*pp)
469 	{	free_ent(*pp);
470 		*pp = (struct ent *)0;
471 	}
472     }
473 
474     /* move the rows, put the deleted, but now empty, row at the end */
475     for (; r < maxrows - 1; r++) {
476 	row_hidden[r] = row_hidden[r+1];
477 	tbl[r] = tbl[r+1];
478 	pp = ATBL(tbl, r, 0);
479 	for (c = 0; c < maxcols; c++, pp++)
480 		if (*pp)
481 			(*pp)->row = r;
482     }
483     tbl[r] = tmprow;
484 
485     maxrow--;
486     FullUpdate++;
487     modflg++;
488 }
489 
490 void
opencol(cs,numcol)491 opencol (cs, numcol)
492 int	cs;
493 int	numcol;
494 {
495     register r;
496     register struct ent **pp;
497     register c;
498     register lim = maxcol-cs+1;
499     int i;
500 
501     if (cs > maxcol)
502 	maxcol = cs;
503     maxcol += numcol;
504 
505     if ((maxcol >= maxcols - 1) && !growtbl(GROWCOL, 0, maxcol))
506 		return;
507 
508     for (i = maxcol; i > cs; i--) {
509 	fwidth[i] = fwidth[i-numcol];
510 	precision[i] = precision[i-numcol];
511 	realfmt[i] = realfmt[i-numcol];
512 	col_hidden[i] = col_hidden[i-numcol];
513     }
514     for (c = cs; c - cs < numcol; c++)
515     {	fwidth[c] = DEFWIDTH;
516 	precision[c] =  DEFPREC;
517 	realfmt[c] = DEFREFMT;
518     }
519 
520     for (r=0; r<=maxrow; r++) {
521 	pp = ATBL(tbl, r, maxcol);
522 	for (c=lim; --c>=0; pp--)
523 	    if ((pp[0] = pp[-numcol]) != NULL)
524 		pp[0]->col += numcol;
525 
526 	pp = ATBL(tbl, r, cs);
527 	for (c = cs; c - cs < numcol; c++, pp++)
528 		*pp = (struct ent *)0;
529     }
530     FullUpdate++;
531     modflg++;
532 }
533 
534 /* delete group of columns (1 or more) */
535 void
closecol(cs,numcol)536 closecol (cs, numcol)
537 int	cs;
538 int	numcol;
539 {
540     register r;
541     register struct ent **pp;
542     register struct ent *q;
543     register c;
544     register lim = maxcol-cs;
545     int i;
546     char buf[50];
547 
548     if (lim - numcol < -1)
549     {	(void) sprintf(buf, "Can't delete %d column%s %d columns left", numcol,
550 			(numcol > 1 ? "s," : ","), lim+1);
551 	scerror(buf);
552 	return;
553     }
554     if (any_locked_cells(0, curcol, maxrow, curcol + numcol - 1)) {
555 	scerror("Locked cells encountered. Nothing changed");
556 	return;
557     }
558     flush_saved();
559     erase_area(0, curcol, maxrow, curcol + numcol - 1);
560     sync_refs();
561 
562     /* clear then copy the block left */
563     lim = maxcols - numcol - 1;
564     for (r=0; r<=maxrow; r++) {
565 	for (c = cs; c - cs < numcol; c++)
566 		if ((q = *ATBL(tbl, r, c)) != NULL)
567 			free_ent(q);
568 
569 	pp = ATBL(tbl, r, cs);
570 	for (c=cs; c <= lim; c++, pp++)
571 	{   if (c > lim)
572 		*pp = (struct ent *)0;
573 	    else
574 	    if ((pp[0] = pp[numcol]) != NULL)
575 		pp[0]->col -= numcol;
576 	}
577 
578 	c = numcol;
579 	for (; --c >= 0; pp++)
580 		*pp = (struct ent *)0;
581     }
582 
583     for (i = cs; i < maxcols - numcol - 1; i++) {
584 	fwidth[i] = fwidth[i+numcol];
585 	precision[i] = precision[i+numcol];
586 	realfmt[i] = realfmt[i+numcol];
587 	col_hidden[i] = col_hidden[i+numcol];
588     }
589     for (; i < maxcols - 1; i++) {
590 	fwidth[i] = DEFWIDTH;
591 	precision[i] = DEFPREC;
592 	realfmt[i] = DEFREFMT;
593 	col_hidden[i] = FALSE;
594     }
595 
596     maxcol -= numcol;
597     FullUpdate++;
598     modflg++;
599 }
600 
601 void
doend(rowinc,colinc)602 doend(rowinc, colinc)
603 int rowinc, colinc;
604 {
605     register struct ent *p;
606     int r, c;
607 
608     if (VALID_CELL(p, currow, curcol)) {
609 	r = currow + rowinc;
610 	c = curcol + colinc;
611 	if (r >= 0 && r < maxrows &&
612 	    c >= 0 && c < maxcols &&
613 	    !VALID_CELL(p, r, c)) {
614 		currow = r;
615 		curcol = c;
616 	}
617     }
618 
619     if (!VALID_CELL(p, currow, curcol)) {
620         switch (rowinc) {
621         case -1:
622 	    while (!VALID_CELL(p, currow, curcol) && currow > 0)
623 		currow--;
624 	    break;
625         case  1:
626 	    while (!VALID_CELL(p, currow, curcol) && currow < maxrows-1)
627 		currow++;
628 	    break;
629         case  0:
630             switch (colinc) {
631  	    case -1:
632 	        while (!VALID_CELL(p, currow, curcol) && curcol > 0)
633 		    curcol--;
634 	        break;
635  	    case  1:
636 	        while (!VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
637 		    curcol++;
638 	        break;
639 	    }
640             break;
641         }
642 
643 	scerror ("");	/* clear line */
644 	return;
645     }
646 
647     switch (rowinc) {
648     case -1:
649 	while (VALID_CELL(p, currow, curcol) && currow > 0)
650 	    currow--;
651 	break;
652     case  1:
653 	while (VALID_CELL(p, currow, curcol) && currow < maxrows-1)
654 	    currow++;
655 	break;
656     case  0:
657 	switch (colinc) {
658 	case -1:
659 	    while (VALID_CELL(p, currow, curcol) && curcol > 0)
660 		curcol--;
661 	    break;
662 	case  1:
663 	    while (VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
664 		curcol++;
665 	    break;
666 	}
667 	break;
668     }
669     if (!VALID_CELL(p, currow, curcol)) {
670         currow -= rowinc;
671         curcol -= colinc;
672     }
673 }
674 
675 /* Modified 9/17/90 THA to handle more formats */
676 void
doformat(c1,c2,w,p,r)677 doformat(c1,c2,w,p,r)
678 int c1,c2,w,p,r;
679 {
680     register int i;
681     int crows = 0;
682     int ccols = c2;
683 
684     if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ;
685     if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ;
686 
687     if (w > maintextcols - RESCOL - 2) {
688 	sprintf(stringbuf,"Format too large - Maximum = %d", maintextcols - RESCOL - 2);
689 	scerror(stringbuf);
690 	w = maintextcols - RESCOL - 2;
691     }
692 
693     if (p > w) {
694 	scerror("Precision too large");
695 	p = w;
696     }
697 
698     checkbounds(&crows, &ccols);
699     if (ccols < c2) {
700 	sprintf(stringbuf,"Format statement failed to create implied column %d", c2);
701 	scerror(stringbuf);
702 	return;
703     }
704 
705     for(i = c1; i<=c2; i++)
706 		fwidth[i] = w, precision[i] = p, realfmt[i] = r;
707 
708     FullUpdate++;
709     modflg++;
710 }
711 
712 static	void
print_options(f)713 print_options(f)
714 FILE *f;
715 {
716     if(
717        autocalc &&
718        propagation == 10 &&
719        calc_order == BYROWS &&
720        !numeric &&
721        prescale == 1.0 &&
722        !extfunc &&
723        showcell &&
724        showtop &&
725        tbl_style == 0 &&
726        craction == 0 &&
727        rowlimit == -1 &&
728        collimit == -1
729       )
730 		return;		/* No reason to do this */
731 
732     (void) fprintf(f, "set");
733     if(!autocalc)
734 	(void) fprintf(f," !autocalc");
735     if(propagation != 10)
736 	(void) fprintf(f, " iterations = %d", propagation);
737     if(calc_order != BYROWS )
738 	(void) fprintf(f, " bycols");
739     if (numeric)
740 	(void) fprintf(f, " numeric");
741     if (prescale != 1.0)
742 	(void) fprintf(f, " prescale");
743     if (extfunc)
744 	(void) fprintf(f, " extfun");
745     if (!showcell)
746 	(void) fprintf(f, " !cellcur");
747     if (!showtop)
748 	(void) fprintf(f, " !toprow");
749     if (tbl_style)
750 	(void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" :
751 					tbl_style == LATEX ? "latex" :
752 					tbl_style == SLATEX ? "slatex" :
753 					tbl_style == TEX ? "tex" :
754 					tbl_style == FRAME ? "frame" : "0" );
755     if (craction)
756 	(void) fprintf(f, " craction = %d", craction);
757     if (rowlimit >= 0)
758 	(void) fprintf(f, " rowlimit = %d", rowlimit);
759     if (collimit >= 0)
760 	(void) fprintf(f, " collimit = %d", collimit);
761     (void) fprintf(f, "\n");
762 }
763 
764 /* output the spreadsheet in a printer-ready form, vs sc commands */
765 void
printfile(fname,r0,c0,rn,cn)766 printfile (fname, r0, c0, rn, cn)
767 char *fname;
768 int r0, c0, rn, cn;
769 {
770     FILE *f;
771     static char *pline = NULL;		/* only malloc once, malloc is slow */
772     static unsigned fbufs_allocated = 0;
773     int plinelim;
774     int pid;
775     int fieldlen = 0;
776     int nextcol = 0;
777     register row, col;
778     register struct ent **pp;
779 
780 	/*
781 	 * don't add a .asc if the user gave us a specific name
782 	 * what_file() scmalloc's space
783 	 */
784     if (*fname != '\0')
785 	fname = what_file(fname, NULL);
786     else
787 	fname = what_file(curfile, ".asc");
788 
789     if ((strcmp(fname, curfile) == 0) &&
790 	!yn_ask("Confirm that you want to destroy the output file: (y,n)"))
791     {
792 	scxfree(fname);
793 	return;
794     }
795 
796     if (!pline && (pline = scxmalloc((unsigned)(FBUFLEN *
797 					++fbufs_allocated))) == (char *)NULL)
798     {   scerror("Malloc failed in printfile()");
799 	scxfree(fname);
800         return;
801     }
802 
803     if ((f = openout(fname, &pid)) == (FILE *)0)
804     {	sprintf(stringbuf, "Can't create file \"%s\"", fname);
805 	scerror(stringbuf);
806 	scxfree(fname);
807 	return;
808     }
809     scxfree(fname);
810 
811     for (row=r0;row<=rn; row++) {
812 	register c = 0;
813 
814 	if (row_hidden[row])
815 	    continue;
816 
817 	pline[plinelim=0] = '\0';
818 	for (pp = ATBL(tbl, row, col=c0); col<=cn;
819 	        pp += nextcol-col, col = nextcol, c += fieldlen) {
820 
821 	    nextcol = col+1;
822 	    if (col_hidden[col]) {
823 		fieldlen = 0;
824 		continue;
825 	    }
826 
827 	    fieldlen = fwidth[col];
828 	    if (*pp) {
829 		char *s;
830 
831 		/*
832 		 * dynamically allocate pline, making sure we are not
833 		 * attempting to write 'out of bounds'.
834 		 */
835 		while(c > (fbufs_allocated * FBUFLEN)) {
836 		  if((pline = scxrealloc
837 			       ((char *)pline,
838 				(unsigned)(FBUFLEN * ++fbufs_allocated)))
839 		     == NULL)
840 		  {
841 		    scerror ("Realloc failed in printfile()");
842 		    return;
843 		  }
844 		}
845 		while (plinelim<c) pline[plinelim++] = ' ';
846 		plinelim = c;
847 		if ((*pp)->flags&is_valid) {
848 		    while(plinelim + fwidth[col] >
849 			  (fbufs_allocated * FBUFLEN)) {
850 		      if((pline = ((char *)scxrealloc
851 				   ((char *)pline,
852 				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
853 			 == NULL) {
854 			scerror ("Realloc failed in printfile()");
855 			return;
856 		      }
857 		    }
858 		    if ((*pp)->cellerror)
859 			(void) sprintf (pline+plinelim, "%*s",
860 				fwidth[col],
861 			((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
862 		    else
863 		    {
864 		      if ((*pp)->format) {
865 	   	        char field[FBUFLEN];
866 			format ((*pp)->format, (*pp)->v, field,
867 				       sizeof(field));
868 			(void) sprintf (pline+plinelim, "%*s", fwidth[col],
869 					field);
870 		      } else {
871 	   	        char field[FBUFLEN];
872 			(void) engformat(realfmt[col], fwidth[col],
873                                              precision[col], (*pp) -> v,
874                                              field, sizeof(field));
875 			(void) sprintf (pline+plinelim, "%*s", fwidth[col],
876 				       field);
877 		      }
878 		    }
879 		    plinelim += strlen (pline+plinelim);
880 		}
881 		if ((s = (*pp)->label) != NULL) {
882 		    int slen;
883 		    char *start, *last;
884 		    register char *fp;
885 		    struct ent *nc;
886 
887 		    /*
888 		     * Figure out if the label slops over to a blank field. A
889 		     * string started with backslash is defining a repetition
890 		     * char
891 		     */
892 		    slen = strlen(s);
893 		    if ((*s == '\\') && (*(s+1) != '\0'))
894 			slen = fwidth[col];
895 
896 		    /* a label in the last writeable column gets all the
897 		     * space it needs
898 		     */
899 		    if (col == cn)
900 			fieldlen = slen;
901 
902 		    while (slen > fieldlen && nextcol <= cn &&
903 			    !((nc = lookat(row,nextcol))->flags & is_valid) &&
904 			    !(nc->label)) {
905 
906 	                if (!col_hidden[nextcol])
907 		 	    fieldlen += fwidth[nextcol];
908 
909 			nextcol++;
910 		    }
911 		    if (slen > fieldlen)
912 			slen = fieldlen;
913 
914 		    while(c + fieldlen + 2 > (fbufs_allocated * FBUFLEN)) {
915 		      if((pline = ((char *)scxrealloc
916 				   ((char *)pline,
917 				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
918 			 == NULL) {
919 			scerror ("scxrealloc failed in printfile()");
920 			return;
921 		      }
922 		    }
923 
924 		    /* Now justify and print */
925 		    start = (*pp)->flags & is_leftflush ? pline + c
926 					: pline + c + fieldlen - slen;
927 		    if( (*pp)->flags & is_label )
928 			start = pline + (c + ((fwidth[col]>slen)?(fwidth[col]-slen)/2:0));
929 		    last = pline + c + fieldlen;
930 		    fp = plinelim < c ? pline + plinelim : pline + c;
931 		    while (fp < start)
932 			*fp++ = ' ';
933 		    if( *s == '\\' && *(s+1)!= '\0' ) {
934 			char *strt;
935 			strt = ++s;
936 
937 			while(slen--) {
938 				*fp++ = *s++; if( *s == '\0' ) s = strt;
939 			}
940 		    }
941 		    else
942 		    while (slen--)
943 			*fp++ = *s++;
944 
945 		    if (!((*pp)->flags & is_valid) || fieldlen != fwidth[col])
946 			while(fp < last)
947 			    *fp++ = ' ';
948 		    if (plinelim < fp - pline)
949 			plinelim = fp - pline;
950 		}
951 	    }
952 	}
953 	pline[plinelim++] = '\n';
954 	pline[plinelim] = '\0';
955 	(void) fputs (pline, f);
956     }
957 
958     closeout(f, pid);
959 }
960 
961 char *
printfile_suffix()962 printfile_suffix()
963 {
964 	char	*suffix = "";
965 
966 	switch (tbl_style)
967 	{
968 	  case 0:	suffix = ".cln";
969 			break;
970 	  case TBL:	suffix = ".tbl";
971 			break;
972 	  case LATEX:	suffix = ".lat";
973 			break;
974 	  case TEX:	suffix = ".tex";
975 			break;
976 	  case FRAME:	suffix = ".fram";
977 			break;
978 	}
979 	return suffix;
980 }
981 
982 void
tblprintfile(fname,r0,c0,rn,cn)983 tblprintfile (fname, r0, c0, rn, cn)
984 char *fname;
985 int r0, c0, rn, cn;
986 {
987     FILE *f;
988     int	pid;
989     register int row, col;
990     register struct ent **pp;
991     char coldelim = DEFCOLDELIM;
992 
993 	/* strip off the .sc ending and add a suffix */
994     fname = what_file(fname, printfile_suffix()); /* scmalloc's space */
995 
996     if ((strcmp(fname, curfile) == 0) &&
997 	!yn_ask("Confirm that you want to destroy the table output: (y,n)"))
998     {	scxfree(fname);
999 	return;
1000     }
1001 
1002     if ((f = openout(fname, &pid)) == (FILE *)0)
1003     {	sprintf(stringbuf, "Can't create file \"%s\"", fname);
1004 	scerror(stringbuf);
1005 	scxfree(fname);
1006 	return;
1007     }
1008 
1009     if ( tbl_style == TBL ) {
1010 	fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname);
1011 	fprintf(f,"tab(%c);\n",coldelim);
1012 	for (col=c0;col<=cn; col++) fprintf(f," n");
1013 	fprintf(f, ".\n");
1014     }
1015     else if ( tbl_style == LATEX ) {
1016 	fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname);
1017 	for (col=c0;col<=cn; col++) fprintf(f,"c");
1018 	fprintf(f, "}\n");
1019 	coldelim = '&';
1020     }
1021     else if ( tbl_style == SLATEX ) {
1022 	fprintf(f,"%% ** %s spreadsheet output\n!begin<tabular><",progname);
1023 	for (col=c0;col<=cn; col++) fprintf(f,"c");
1024 	fprintf(f, ">\n");
1025 	coldelim = '&';
1026     }
1027     else if ( tbl_style == TEX ) {
1028 	fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n",
1029 		progname, cn-c0+1);
1030 	coldelim = '&';
1031     }
1032     else if ( tbl_style == FRAME ) {
1033 	fprintf(f,"<MIFFile 3.00> # generated by the sc spreadsheet calculator\n");
1034 	fprintf(f,"<Tbls\n");
1035 	fprintf(f," <Tbl \n");
1036 	fprintf(f,"  <TblID 1> # This table's ID is 1\n");
1037 	fprintf(f,"  <TblFormat \n");
1038 	fprintf(f,"   <TblTag `Format A'> # Table Format Catalog\n");
1039 	fprintf(f,"  > # end of TblFormat\n");
1040 	fprintf(f,"  <TblNumColumns %d> # Has %d columns\n",cn-c0+1,cn-c0+1);
1041 	fprintf(f,"  <TblTitleContent\n");
1042 	fprintf(f,"   <Para\n");
1043 	fprintf(f,"    <PgfTag `TableTitle'> # Forces lookup in Paragraph Format Catalog\n");
1044 	fprintf(f,"    <ParaLine\n");
1045 	fprintf(f,"     <String `%s'>\n",fname);
1046 	fprintf(f,"    > # end of ParaLine\n");
1047 	fprintf(f,"   > # end of Para\n");
1048 	fprintf(f,"  > # end of TblTitleContent\n");
1049 	fprintf(f,"  <TblH # The heading\n");
1050 	fprintf(f,"   <Row # The heading row\n");
1051 	for (col=c0; col <= cn; col++) {
1052 	    fprintf(f,"    <Cell <CellContent <Para # Cell in column \n");
1053 	    fprintf(f,"       <PgfTag `CellHeading'> # in Paragraph Format Catalog\n");
1054 	    fprintf(f,"       <ParaLine <String `%c'>>\n",'A'+col);
1055 	    fprintf(f,"    >>> # end of Cell\n");
1056 	}
1057 	fprintf(f,"   > # end of Row\n");
1058 	fprintf(f,"  > # end of TblH\n");
1059 	fprintf(f,"  <TblBody # The body\n");
1060     }
1061 
1062     for (row=r0; row<=rn; row++) {
1063 	if ( tbl_style == TEX )
1064 	    (void) fprintf (f, "\\+");
1065 	else if ( tbl_style == FRAME ) {
1066 	    fprintf(f,"   <Row # The next body row\n");
1067 	}
1068 
1069 	for (pp = ATBL(tbl, row, col=c0); col<=cn; col++, pp++) {
1070 	    if ( tbl_style == FRAME ) {
1071 		fprintf(f,"    <Cell <CellContent <Para\n");
1072 		fprintf(f,"       <PgfTag `CellBody'> # in Paragraph Format Catalog\n");
1073 		fprintf(f,"       <ParaLine <String `");
1074 	    }
1075 	    if (*pp) {
1076 		char *s;
1077 		if ((*pp)->flags&is_valid) {
1078 		    if ((*pp)->cellerror) {
1079 			(void) fprintf (f, "%*s",
1080 					fwidth[col],
1081 			((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
1082 		    }
1083 		    else
1084 		    if ((*pp)->format) {
1085 		        char field[FBUFLEN];
1086 
1087 			(void) format ((*pp)->format, (*pp)->v, field,
1088 				       sizeof(field));
1089 			unspecial (f, field, coldelim);
1090 		    } else {
1091 		        char field[FBUFLEN];
1092                         (void) engformat(realfmt[col], fwidth[col],
1093                                              precision[col], (*pp) -> v,
1094                                              field, sizeof(field));
1095 			unspecial (f, field, coldelim);
1096 		    }
1097 		}
1098 		if ((s = (*pp)->label) != NULL) {
1099 	            unspecial (f, s, coldelim);
1100 		}
1101 	    }
1102 	    if (tbl_style == FRAME) {
1103 		fprintf(f, "'>>\n");
1104 		fprintf(f,"    >>> # end of Cell\n");
1105 	    }
1106 	    if ( col < cn )
1107 		if (tbl_style != FRAME)
1108 		    (void) fprintf(f,"%c", coldelim);
1109 	}
1110 	if ( tbl_style == LATEX ) {
1111 		if ( row < rn ) (void) fprintf (f, "\\\\");
1112 	}
1113 	else if ( tbl_style == SLATEX ) {
1114 		if ( row < rn ) (void) fprintf (f, "!!");
1115 	}
1116 	else if ( tbl_style == TEX ) {
1117 		(void) fprintf (f, "\\cr");
1118 	}
1119 	else if ( tbl_style == FRAME ) {
1120 	    fprintf(f,"   > # end of Row\n");
1121 	}
1122 	(void) fprintf (f,"\n");
1123     }
1124 
1125     if ( tbl_style == TBL )
1126     (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname);
1127     else if ( tbl_style == LATEX )
1128     (void) fprintf (f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname);
1129     else if ( tbl_style == SLATEX )
1130     (void) fprintf (f,"!end<tabular>\n%% ** end of %s spreadsheet output\n", progname);
1131     else if ( tbl_style == TEX )
1132     (void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname);
1133     else if ( tbl_style == FRAME ) {
1134 	fprintf(f,"  > # end of TblBody\n");
1135 	fprintf(f," ># end of Tbl\n");
1136 	fprintf(f,"> # end of Tbls\n");
1137 	fprintf(f,"<TextFlow <Para \n");
1138 	fprintf(f,"  <PgfTag Body> \n");
1139 	fprintf(f,"  <ParaLine <ATbl 1>> # Reference to table ID 1\n");
1140 	fprintf(f,">>\n");
1141     }
1142 
1143     scxfree(fname);
1144     closeout(f, pid);
1145 }
1146 
1147 /* unspecial (backquote) things that are special chars in a table */
1148 static	void
unspecial(f,str,delim)1149 unspecial(f, str, delim)
1150 FILE	*f;
1151 char	*str;
1152 int	delim;
1153 {
1154 	if( *str == '\\' ) str++; /* delete wheeling string operator, OK? */
1155 	while (*str)
1156 	{	if (((tbl_style == LATEX) || (tbl_style == SLATEX) ||
1157 		    (tbl_style == TEX)) &&
1158 		    ((*str == delim) || (*str == '$') || (*str == '#') ||
1159 		    (*str == '%') || (*str == '{') || (*str == '}') ||
1160 		    (*str == '[') || (*str == ']') || (*str == '&')))
1161 			putc('\\', f);
1162 		putc(*str, f);
1163 		str++;
1164 	}
1165 }
1166 
1167 struct enode *
copye(e,Rdelta,Cdelta)1168 copye (e, Rdelta, Cdelta)
1169 register struct enode *e;
1170 int Rdelta, Cdelta;
1171 {
1172     register struct enode *ret;
1173 
1174     if (e == (struct enode *)0) {
1175         ret = (struct enode *)0;
1176     } else if (e->op & REDUCE) {
1177 	int newrow, newcol;
1178 	if (freeenodes)
1179 	{	ret = freeenodes;
1180 		freeenodes = ret->e.o.left;
1181 	}
1182 	else
1183 		ret = (struct enode *) scxmalloc ((unsigned) sizeof (struct enode));
1184 	ret->op = e->op;
1185 	newrow=e->e.r.left.vf & FIX_ROW ? e->e.r.left.vp->row :
1186 					  e->e.r.left.vp->row+Rdelta;
1187 	newcol=e->e.r.left.vf & FIX_COL ? e->e.r.left.vp->col :
1188 					  e->e.r.left.vp->col+Cdelta;
1189 	ret->e.r.left.vp = lookat (newrow, newcol);
1190 	ret->e.r.left.vf = e->e.r.left.vf;
1191 	newrow=e->e.r.right.vf & FIX_ROW ? e->e.r.right.vp->row :
1192 					   e->e.r.right.vp->row+Rdelta;
1193 	newcol=e->e.r.right.vf & FIX_COL ? e->e.r.right.vp->col :
1194 					   e->e.r.right.vp->col+Cdelta;
1195 	ret->e.r.right.vp = lookat (newrow, newcol);
1196 	ret->e.r.right.vf = e->e.r.right.vf;
1197     } else {
1198 	if (freeenodes)
1199 	{	ret = freeenodes;
1200 		freeenodes = ret->e.o.left;
1201 	}
1202 	else
1203 		ret = (struct enode *) scxmalloc ((unsigned) sizeof (struct enode));
1204 	ret->op = e->op;
1205 	switch (ret->op) {
1206 	case 'v':
1207 		{
1208 		    int newrow, newcol;
1209 		    newrow=e->e.v.vf & FIX_ROW ? e->e.v.vp->row :
1210 						 e->e.v.vp->row+Rdelta;
1211 		    newcol=e->e.v.vf & FIX_COL ? e->e.v.vp->col :
1212 						 e->e.v.vp->col+Cdelta;
1213 		    ret->e.v.vp = lookat (newrow, newcol);
1214 		    ret->e.v.vf = e->e.v.vf;
1215 		    break;
1216 		}
1217 	case 'k':
1218 		ret->e.k = e->e.k;
1219 		break;
1220 	case 'f':
1221 		ret->e.o.right = copye (e->e.o.right,0,0);
1222 		ret->e.o.left = (struct enode *)0;
1223  		break;
1224 	case '$':
1225 		ret->e.s = scxmalloc((unsigned) strlen(e->e.s)+1);
1226 		(void) strcpy(ret->e.s, e->e.s);
1227 		break;
1228 	default:
1229 		ret->e.o.right = copye (e->e.o.right,Rdelta,Cdelta);
1230 		ret->e.o.left = copye (e->e.o.left,Rdelta,Cdelta);
1231 		break;
1232 	}
1233     }
1234     return ret;
1235 }
1236 
1237 /*
1238  * sync_refs and syncref are used to remove references to
1239  * deleted struct ents.  Note that the deleted structure must still
1240  * be hanging around before the call, but not referenced by an entry
1241  * in tbl.  Thus the free_ent calls in sc.c
1242  */
1243 void
sync_refs()1244 sync_refs ()
1245 {
1246     register i,j;
1247     register struct ent *p;
1248     sync_ranges();
1249     for (i=0; i<=maxrow; i++)
1250 	for (j=0; j<=maxcol; j++)
1251             {
1252 	    /* if ((p = *ATBL(tbl, i, j)) && p->expr) */
1253 	    p = *ATBL(tbl, i, j);
1254             if (p && p->expr)
1255 		syncref(p->expr);
1256             }
1257 }
1258 
1259 static	void
syncref(e)1260 syncref(e)
1261 register struct enode *e;
1262 {
1263     if (e == (struct enode *)0)
1264 	return;
1265     else if (e->op & REDUCE) {
1266  	e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col);
1267  	e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col);
1268     } else {
1269 	switch (e->op) {
1270 	case 'v':
1271 		e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col);
1272 		break;
1273 	case 'k':
1274 		break;
1275 	case '$':
1276 		break;
1277 	default:
1278 		syncref(e->e.o.right);
1279 		syncref(e->e.o.left);
1280 		break;
1281 	}
1282     }
1283 }
1284 
1285 /* mark a row as hidden */
1286 void
hiderow(arg)1287 hiderow(arg)
1288 int arg;
1289 {
1290     register int r1;
1291     register int r2;
1292 
1293     r1 = currow;
1294     r2 = r1 + arg - 1;
1295     if (r1 < 0 || r1 > r2) {
1296 	scerror ("Invalid range");
1297 	return;
1298     }
1299     if (r2 >= maxrows-1)
1300     {	if (!growtbl(GROWROW, arg+1, 0))
1301 	{	scerror("You can't hide the last row");
1302 		return;
1303 	}
1304     }
1305     FullUpdate++;
1306     modflg++;
1307     while (r1 <= r2)
1308 	row_hidden[r1++] = 1;
1309 }
1310 
1311 /* mark a column as hidden */
1312 void
hidecol(arg)1313 hidecol(arg)
1314 int arg;
1315 {
1316     register int c1;
1317     register int c2;
1318 
1319     c1 = curcol;
1320     c2 = c1 + arg - 1;
1321     if (c1 < 0 || c1 > c2) {
1322 	scerror ("Invalid range");
1323 	return;
1324     }
1325     if (c2 >= maxcols-1)
1326     {	if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1))
1327 	{	scerror("You can't hide the last col");
1328 		return;
1329 	}
1330     }
1331     FullUpdate++;
1332     modflg++;
1333     while (c1 <= c2)
1334 	col_hidden[c1++] = TRUE;
1335 }
1336 
1337 /* mark a row as not-hidden */
1338 void
showrow(r1,r2)1339 showrow(r1, r2)
1340 int r1, r2;
1341 {
1342     if (r1 < 0 || r1 > r2) {
1343 	scerror ("Invalid range");
1344 	return;
1345     }
1346     if (r2 > maxrows-1) {
1347 	r2 = maxrows-1;
1348     }
1349     FullUpdate++;
1350     modflg++;
1351     while (r1 <= r2)
1352 	row_hidden[r1++] = 0;
1353 }
1354 
1355 /* mark a column as not-hidden */
1356 void
showcol(c1,c2)1357 showcol(c1, c2)
1358 int c1, c2;
1359 {
1360     if (c1 < 0 || c1 > c2) {
1361 	scerror ("Invalid range");
1362 	return;
1363     }
1364     if (c2 > maxcols-1) {
1365 	c2 = maxcols-1;
1366     }
1367     FullUpdate++;
1368     modflg++;
1369     while (c1 <= c2)
1370 	col_hidden[c1++] = FALSE;
1371 }
1372 
1373 /* Open the output file, setting up a pipe if needed */
1374 FILE *
openout(fname,rpid)1375 openout(fname, rpid)
1376 char *fname;
1377 int *rpid;
1378 {
1379     int pipefd[2];
1380     int pid;
1381     FILE *f = NULL;
1382     char *efname;
1383 
1384     while (*fname && (*fname == ' '))  /* Skip leading blanks */
1385 	fname++;
1386 
1387     if (*fname != '|') {		/* Open file if not pipe */
1388 	*rpid = 0;
1389 
1390 	efname = findhome(fname);
1391 #ifdef DOBACKUPS
1392 	if (!backup_file(efname) &&
1393 	    (yn_ask("Could not create backup copy, Save anyhow?: (y,n)") != 1))
1394 		return(0);
1395 #endif
1396 	return(fopen(efname, file_mode));
1397     }
1398 
1399 #if defined(MSDOS)
1400     scerror("Piping not available under MS-DOS\n");
1401     return(0);
1402 #else
1403     fname++;				/* Skip | */
1404     if ( pipe (pipefd) < 0) {
1405 	scerror("Can't make pipe to child");
1406 	*rpid = 0;
1407 	return(0);
1408     }
1409 
1410     if (!using_X)
1411 	deraw();
1412 
1413 #ifdef VMS
1414     fprintf(stderr, "No son tasks available yet under VMS--sorry\n");
1415 #else /* VMS */
1416 
1417     if ((pid=fork()) == 0)			  /* if child  */
1418     {
1419 	(void) close (0);			  /* close stdin */
1420 	(void) close (pipefd[1]);
1421 	(void) dup (pipefd[0]);		  /* connect to pipe input */
1422 	(void) signal (SIGINT, SIG_DFL);	  /* reset */
1423 	(void) execl ("/bin/sh", "sh", "-c", fname, 0);
1424 	exit (-127);
1425     }
1426     else				  /* else parent */
1427     {
1428 	*rpid = pid;
1429 	if ((f = fdopen (pipefd[1], "w")) == (FILE *)0)
1430 	{
1431 	    (void) kill (pid, -9);
1432 	    scerror ("Can't fdopen output");
1433 	    (void) close (pipefd[1]);
1434 	    *rpid = 0;
1435 	    return(0);
1436 	}
1437     }
1438 #endif /* VMS */
1439     return(f);
1440 #endif /* MSDOS */
1441 }
1442 
1443 /* close a file opened by openout(), if process wait for return */
1444 void
closeout(f,pid)1445 closeout(f, pid)
1446 FILE *f;
1447 int pid;
1448 {
1449     int temp;
1450 
1451     (void) fclose (f);
1452 #if !defined(MSDOS)
1453     if (pid) {
1454          while (pid != wait(&temp)) /**/;
1455 	 (void) printf("Press RETURN to continue ");
1456 	 (void) fflush(stdout);
1457 	 (void) nmgetch();
1458 	 if (!using_X)
1459 		goraw();
1460     }
1461 #endif /* MSDOS */
1462 }
1463 
1464 void
copyent(n,p,dr,dc)1465 copyent(n,p,dr,dc)
1466 	    register struct ent *n, *p;
1467 	    int dr, dc;
1468 {
1469     if(!n||!p)
1470     {	scerror("internal error");
1471 	return;
1472     }
1473     n -> v = p -> v;
1474     n -> flags = p -> flags;
1475     n -> expr = copye (p -> expr, dr, dc);
1476     n -> label = (char *)0;
1477     if (p -> label) {
1478 	n -> label = scxmalloc ((unsigned) (strlen (p -> label) + 1));
1479 	(void) strcpy (n -> label, p -> label);
1480     }
1481     n -> format = 0;
1482     if (p -> format) {
1483         n -> format = scxmalloc ((unsigned) (strlen (p -> format) + 1));
1484 	(void) strcpy (n -> format, p -> format);
1485     }
1486 }
1487 
1488 /* output ascii sc commands */
1489 void
write_fd(f,r0,c0,rn,cn)1490 write_fd (f, r0, c0, rn, cn)
1491 register FILE *f;
1492 int r0, c0, rn, cn;
1493 {
1494     register struct ent **pp;
1495     register r, c;
1496 
1497     (void) fprintf (f, "# This data file was generated by the Spreadsheet ");
1498     (void) fprintf (f, "Calculator.\n");
1499     (void) fprintf (f, "# You almost certainly shouldn't edit it.\n\n");
1500     graphic_write_defn(f);	/* write graph definitions, if any exist */
1501     print_options(f);
1502     for (c=0; c<maxcols; c++)
1503 	if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC || realfmt[c] != DEFREFMT )
1504 	    (void) fprintf (f, "format %s %d %d %d\n",coltoa(c),fwidth[c],precision[c],realfmt[c]);
1505     for (c=c0; c<cn; c++) {
1506         if (col_hidden[c]) {
1507             (void) fprintf(f, "hide %s\n", coltoa(c));
1508         }
1509     }
1510     for (r=r0; r<=rn; r++) {
1511 	if (row_hidden[r]) {
1512 	    (void) fprintf(f, "hide %d\n", r);
1513 	}
1514     }
1515 
1516     write_range(f);
1517 
1518     if (mdir)
1519 	    (void) fprintf(f, "mdir \"%s\"\n", mdir);
1520     for (r=r0; r<=rn; r++) {
1521 	pp = ATBL(tbl, r, c0);
1522 	for (c=c0; c<=cn; c++, pp++)
1523 	    if (*pp) {
1524 		if ((*pp)->label || (*pp)->flags&is_strexpr) {
1525 		    edits(r,c);
1526 		    (void) fprintf(f, "%s\n",line);
1527 		}
1528 		if ((*pp)->flags&is_valid) {
1529 		    editv (r, c);
1530 		    (void) fprintf (f, "%s\n",line);
1531 		}
1532 		if ((*pp)->format) {
1533 		    editfmt (r, c);
1534 		    (void) fprintf (f, "%s\n",line);
1535 		}
1536 		if ((*pp)->flags&is_locked)
1537 		    (void) fprintf(f, "lock %s%d\n", coltoa((*pp)->col),
1538 						     (*pp)->row) ;
1539 	    }
1540     }
1541     if (rndinfinity)
1542 	fprintf(f, "set rndinfinity\n");
1543     fprintf(f, "goto %s\n", v_name( currow, curcol ) );
1544 }
1545 
1546 int
writefile(fname,r0,c0,rn,cn)1547 writefile (fname, r0, c0, rn, cn)
1548 char *fname;
1549 int r0, c0, rn, cn;
1550 {
1551     register FILE *f;
1552     int pid, cret;
1553 
1554     if (*fname == '\0')
1555 	fname = curfile;
1556     fname = what_file(fname, NULL);
1557 
1558 #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH)
1559     if (Crypt) {
1560 	cret = cwritefile(fname, r0, c0, rn, cn);
1561 	scxfree(fname);
1562 	return(cret);
1563     }
1564 #endif /* VMS */
1565 
1566     if ((f= openout(fname, &pid)) == (FILE *)0)
1567     {	sprintf(stringbuf, "Can't create file \"%s\"", fname);
1568 	scerror(stringbuf);
1569 	scxfree(fname);
1570 	return (-1);
1571     }
1572 
1573     write_fd(f, r0, c0, rn, cn);
1574 
1575     closeout(f, pid);
1576 
1577     if (!pid) {
1578         (void) strcpy(curfile, fname);
1579         modflg = 0;
1580 	sprintf(stringbuf,"File \"%s\" written.",curfile);
1581 	scerror(stringbuf);
1582     }
1583 
1584     scxfree(fname);
1585     return (0);
1586 }
1587 
1588 void
readfile(fname,eraseflg)1589 readfile (fname,eraseflg)
1590 char *fname;
1591 int eraseflg;
1592 {
1593     register FILE *f;
1594     int tempautolabel;
1595 
1596     tempautolabel = autolabel;		/* turn off auto label */
1597     autolabel = 0;			/* when reading a file  */
1598 
1599     if (*fname == '\0')
1600 	fname = curfile;
1601     fname = what_file(fname, NULL);
1602 
1603 #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH)
1604     if (Crypt)  {
1605 	creadfile(fname, eraseflg);
1606 	scxfree(fname);
1607 	return;
1608     }
1609 #endif /* VMS */
1610 
1611     if (eraseflg && strcmp(fname,curfile) && modcheck(" first"))
1612     {	scxfree(fname);
1613 	return;
1614     }
1615 
1616     if ((f = fopen(findhome(fname), "r")) == (FILE *)0)
1617     {	sprintf(stringbuf, "Can't read file \"%s\"", fname);
1618 	scerror(stringbuf);
1619 	scxfree(fname);
1620 	return;
1621     }
1622 
1623     if (eraseflg)
1624 	erasedb();
1625 
1626     loading++;
1627     while (fgets(line, sizeof(line), f)) {
1628 	linelim = 0;
1629 	if (line[0] == 'G') 	/* indicates graph definitions */
1630 	    graphic_read_defn(f);
1631 	else if (line[0] != '#') (void) yyparse ();
1632     }
1633     --loading;
1634     (void) fclose (f);
1635     linelim = -1;
1636     modflg++;
1637     if (eraseflg) {
1638 	(void) strcpy(curfile,fname);
1639 	modflg = 0;
1640     }
1641     autolabel = tempautolabel;
1642     EvalAll();
1643     scxfree(fname);
1644 }
1645 
1646 /* read in a file as strings into the startrow at startcol,
1647  * and clipping at endcol.
1648  */
1649 void
readstrfile(fname,startrow,startcol,endrow,endcol)1650 readstrfile(fname, startrow, startcol, endrow, endcol)
1651 char	*fname;
1652 int	startrow, startcol;
1653 int	endrow, endcol;
1654 {
1655     register FILE *f;
1656     int tempautolabel;
1657     struct ent	*e;
1658     int		sr;
1659     char	*cp;
1660 
1661     tempautolabel = autolabel;		/* turn off auto label when */
1662     autolabel = 0;			/* when reading a file  */
1663 
1664     if (*fname == '\0')
1665 	fname = curfile;
1666     fname = what_file(fname, NULL);
1667 
1668     if ((f = fopen(findhome(fname), "r")) == (FILE *)0)
1669     {	sprintf(stringbuf, "Can't read file \"%s\"", fname);
1670 	scerror(stringbuf);
1671 	scxfree(fname);
1672 	return;
1673     }
1674 
1675     loading++;
1676 
1677     for (sr = startrow; fgets(line, sizeof(line), f); sr++) {
1678 	if (endrow != -1 && sr > endrow)
1679 		break;
1680 	linelim = 0;
1681 
1682 	/* clip the newline if there is one. */
1683 	cp = &line[strlen(line)-1];
1684 	if (*cp == '\n' || *cp == '\r')
1685 		*cp = 0;
1686 	e = lookat(sr, startcol);
1687 	(void)label(e, line, -1);
1688     }
1689     sprintf(stringbuf, "Read %d lines.", sr-startrow);
1690     scerror(stringbuf);
1691     --loading;
1692     /* go back to where we started */
1693     moveto(startrow, startcol);
1694     (void) fclose (f);
1695     linelim = -1;
1696     modflg++;
1697     autolabel = tempautolabel;
1698     EvalAll();
1699 }
1700 
1701 /* erase the database (tbl, etc.) */
1702 void
erasedb()1703 erasedb ()
1704 {
1705     register r, c;
1706     for (c = 0; c<=maxcol; c++) {
1707 	fwidth[c] = DEFWIDTH;
1708 	precision[c] = DEFPREC;
1709 	realfmt[c] = DEFREFMT;
1710     }
1711 
1712     for (r = 0; r<=maxrow; r++) {
1713 	register struct ent **pp = ATBL(tbl, r, 0);
1714 	for (c=0; c++<=maxcol; pp++)
1715 	    if (*pp) {
1716 		if ((*pp)->expr)  efree ((*pp) -> expr);
1717 		if ((*pp)->label) scxfree ((char *)((*pp) -> label));
1718 		(*pp)->next = freeents;	/* save [struct ent] for reuse */
1719 		freeents = *pp;
1720 		*pp = (struct ent *)0;
1721 	    }
1722     }
1723     maxrow = 0;
1724     maxcol = 0;
1725     clean_range();
1726     FullUpdate++;
1727 }
1728 
1729 /* moves curcol back one displayed column */
1730 void
backcol(arg)1731 backcol(arg)
1732 	int arg;
1733 {
1734     prvmx=lastmx; prvmy=lastmy, prvcol=lastcol; /* line added by Bob Parbs 12-92 */
1735     while (--arg>=0) {
1736 	if (curcol)
1737 	    curcol--;
1738 	else
1739 	{	scerror ("At column A");
1740 		break;
1741 	}
1742 	while(col_hidden[curcol] && curcol)
1743 	    curcol--;
1744     }
1745 }
1746 
1747 /* moves curcol forward one displayed column */
1748 void
forwcol(arg)1749 forwcol(arg)
1750 	int arg;
1751 {
1752     prvmx=lastmx; prvmy=lastmy; prvcol=lastcol; /* line added by Bob Parbs 12-92 */
1753     while (--arg>=0) {
1754 	if (curcol < maxcols - 1)
1755 	    curcol++;
1756 	else
1757 	if (!growtbl(GROWCOL, 0, arg))	/* get as much as needed */
1758 		break;
1759 	else
1760 		curcol++;
1761 	while(col_hidden[curcol]&&(curcol<maxcols-1))
1762 	    curcol++;
1763     }
1764 }
1765 
1766 /* moves currow forward one displayed row */
1767 void
forwrow(arg)1768 forwrow(arg)
1769 	int arg;
1770 {
1771     prvmx=lastmx; prvmy=lastmy; prvcol=lastcol; /* line added by Bob Parbs 12-92 */
1772     while (--arg>=0) {
1773 	if (currow < maxrows - 1)
1774 	    currow++;
1775 	else
1776 	if (!growtbl(GROWROW, arg, 0))	/* get as much as needed */
1777 		break;
1778 	else
1779 		currow++;
1780 	while (row_hidden[currow]&&(currow<maxrows-1))
1781 	    currow++;
1782     }
1783 }
1784 
1785 /* moves currow backward one displayed row */
1786 void
backrow(arg)1787 backrow(arg)
1788 	int arg;
1789 {
1790     prvmx=lastmx; prvmy=lastmy; prvcol=lastcol; /* line added by Bob Parbs 12-92 */
1791     while (--arg>=0) {
1792 	if (currow)
1793 	    currow--;
1794 	else
1795 	{	scerror ("At row zero");
1796 		break;
1797 	}
1798 	while (row_hidden[currow] && currow)
1799 	    currow--;
1800     }
1801 }
1802 
1803 
1804 /*
1805  * Show a cell's label string or expression value.  May overwrite value if
1806  * there is one already displayed in the cell.  Created from old code in
1807  * update(), copied with minimal changes.
1808  */
1809 
1810 void
showstring(string,dirflush,hasvalue,row,col,nextcolp,mxcol,fieldlenp,r,c,do_stand)1811 showstring (string, dirflush, hasvalue, row, col, nextcolp, mxcol, fieldlenp,
1812 	    r, c, do_stand)
1813     char *string;	/* to display */
1814     int dirflush;	/* or rightflush or centered */
1815     int hasvalue;	/* is there a numeric value? */
1816     int row, col;	/* spreadsheet location */
1817     int *nextcolp;	/* value returned through it */
1818     int mxcol;		/* last column displayed? */
1819     int *fieldlenp;	/* value returned through it */
1820     int r, c;		/* screen row and column */
1821     int do_stand;	/* if standout needed */
1822 {
1823     register int nextcol  = *nextcolp;
1824     register int fieldlen = *fieldlenp;
1825 
1826     char field[FBUFLEN];
1827     int  slen;
1828     char *start, *last;
1829     register char *fp;
1830     struct ent *nc;
1831 
1832     /* This figures out if the label is allowed to
1833        slop over into the next blank field */
1834 
1835     slen = strlen (string);
1836     if( *string == '\\' && *(string+1)!= '\0' )
1837 	slen = fwidth[col];
1838     while ((slen > fieldlen) && (nextcol <= mxcol) &&
1839 	   !((nc = lookat (row, nextcol)) -> flags & is_valid) &&
1840 	   !(nc->label)) {
1841 
1842 	if (! col_hidden [nextcol])
1843 	    fieldlen += fwidth [nextcol];
1844 
1845 	nextcol++;
1846     }
1847     if (slen > fieldlen)
1848 	slen = fieldlen;
1849 
1850     /* Now justify and print */
1851     start = (dirflush&is_leftflush) ? field : field + fieldlen - slen;
1852     if( dirflush & is_label )
1853 	start = field + ((slen<fwidth[col])?(fieldlen-slen)/2:0);
1854     last = field+fieldlen;
1855     fp = field;
1856     while (fp < start)
1857 	*fp++ = ' ';
1858     if( *string == '\\'  && *(string+1)!= '\0') {
1859 	char *strt;
1860 	strt = ++string;
1861 
1862 	while(slen--) {
1863 		*fp++ = *string++;
1864 		if( *string == '\0' )
1865 			string = strt;
1866 	}
1867     }
1868     else
1869     while (slen--)
1870 	*fp++ = *string++;
1871 
1872     if ((! hasvalue) || fieldlen != fwidth[col])
1873 	while (fp < last)
1874 	    *fp++ = ' ';
1875     *fp = '\0';
1876 #ifdef HAVE_X11_X_H
1877     if (using_X)
1878     {
1879 	XDrawImageString(dpy, mainwin, do_stand ? maingcreversed : maingc,
1880 		     textcol(c), textrow(r),
1881 		     field, strlen(field));
1882     } else
1883 #endif /* HAVE_X11_X_H */
1884     {
1885 # ifdef VMS
1886 	mvaddstr(r, c, field);	/* this is a macro */
1887 # else
1888 	(void) mvaddstr(r, c, field);
1889 # endif
1890     } /* HAVE_X11_X_H, end curses */
1891 
1892     *nextcolp  = nextcol;
1893     *fieldlenp = fieldlen;
1894 }
1895 
1896 int
etype(e)1897 etype(e)
1898 register struct enode *e;
1899 {
1900     if (e == (struct enode *)0)
1901 	return NUM;
1902     switch (e->op) {
1903     case UPPER: case LOWER: case CAPITAL:
1904     case O_SCONST: case '#': case DATE: case FMT: case STINDEX:
1905     case EXT: case SVAL: case SUBSTR:
1906         return (STR);
1907 
1908     case '?':
1909     case IF:
1910         return(etype(e->e.o.right->e.o.left));
1911 
1912     case 'f':
1913         return(etype(e->e.o.right));
1914 
1915     case O_VAR: {
1916 	register struct ent *p;
1917 	p = e->e.v.vp;
1918 	if (p->expr)
1919 	    return(p->flags & is_strexpr ? STR : NUM);
1920 	else if (p->label)
1921 	    return(STR);
1922 	else
1923 	    return(NUM);
1924 	}
1925 
1926     default:
1927 	return(NUM);
1928     }
1929 }
1930 
1931 /* return 1 if yes given, 0 otherwise */
1932 int
yn_ask(msg)1933 yn_ask(msg)
1934 char	*msg;
1935 {	char ch;
1936 
1937 #ifdef HAVE_X11_X_H
1938 	XEvent event;
1939 	char buffer[8];
1940 
1941 /* I'd rather use line 1 than line 0, because that is where the user will
1942  * be looking most often.  (B.Backman)
1943  */
1944      if (using_X)
1945      {
1946 	clearlines(1,1);
1947 	XDrawImageString(dpy, mainwin, maingc,
1948 			 textcol(0), textrow(1),
1949 			 msg, strlen(msg));
1950 	while (1)
1951 	{
1952 		XPeekEvent(dpy, &event);
1953 
1954 		while (event.type == MotionNotify)
1955 		   XNextEvent(dpy,&event);
1956 		if (event.type == KeyPress)
1957 		{
1958 		   XNextEvent(dpy,&event);
1959 		   if(XLookupString(&(event.xkey),buffer,8,0,0))
1960 		   {
1961 			ch = buffer[0];
1962 			if (ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N')
1963 			{	if (ch == ctl('g') || ch == ESC)
1964 					return(-1);
1965 				scerror("y or n response required");
1966 				return (-1);
1967 			}
1968 			if (ch == 'y' || ch == 'Y')
1969 				return(1);
1970 			else
1971 				return(0);
1972                    }
1973 		}
1974 		else
1975 		{
1976 		   scerror("Y or N keypress is required");
1977 		   return(-1);
1978 		}
1979 	}
1980 	return(-1);	/* never reached */
1981      } else
1982 #endif /* HAVE_X11_X_H */
1983      {
1984 	(void) move (0, 0);
1985 	(void) clrtoeol ();
1986 	(void) addstr (msg);
1987 	(void) refresh();
1988 	ch = nmgetch();
1989 	if ( ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N' ) {
1990 		if (ch == ctl('g') || ch == ESC)
1991 			return(-1);
1992 		scerror("y or n response required");
1993 		return (-1);
1994 	}
1995 	if (ch == 'y' || ch == 'Y')
1996 		return(1);
1997 	else
1998 		return(0);
1999      } /* HAVE_X11_X_H, end curses */
2000 }
2001 
2002 /* expand a ~ in a path to your home directory */
2003 #if !defined(MSDOS) && !defined(VMS)
2004 #include <pwd.h>
2005 #endif
2006 char	*
findhome(path)2007 findhome(path)
2008 char	*path;
2009 {
2010 	static	char	*HomeDir = NULL;
2011 
2012 	if (*path == '~')
2013 	{	char	*pathptr;
2014 		char	tmppath[PATHLEN];
2015 
2016 		if (HomeDir == NULL)
2017 		{	HomeDir = getenv("HOME");
2018 			if (HomeDir == NULL)
2019 				HomeDir = "/";
2020 		}
2021 		pathptr = path + 1;
2022 		if ((*pathptr == '/') || (*pathptr == '\0'))
2023 		{	strcpy(tmppath, HomeDir);
2024 		}
2025 #if !defined(MSDOS) && !defined(VMS)
2026 		else
2027 		{	struct	passwd *pwent;
2028 #ifndef __STDC__
2029 			extern	struct	passwd *getpwnam();
2030 #endif
2031 			char	*namep;
2032 			char	name[50];
2033 
2034 			namep = name;
2035 			while ((*pathptr != '\0') && (*pathptr != '/'))
2036 				*(namep++) = *(pathptr++);
2037 			*namep = '\0';
2038 			if ((pwent = getpwnam(name)) == NULL)
2039 			{	(void) sprintf(path, "Can't find user %s", name);
2040 				return(NULL);
2041 			}
2042 			strcpy(tmppath, pwent->pw_dir);
2043 		}
2044 #endif
2045 		strcat(tmppath, pathptr);
2046 		strcpy(path, tmppath);
2047 	}
2048 	return(path);
2049 }
2050 
2051 #ifdef DOBACKUPS
2052 #include <sys/stat.h>
2053 
2054 #ifdef NOTUSED
2055 /******************************************
2056  * Determine default dir to be printed out,
2057  * based on mdir.  Check for //  **********/
2058 void
get_default_dir(char * tmp)2059 get_default_dir(char *tmp)
2060 {
2061     int length;
2062 
2063     if (mdir)
2064         strcpy(tmp, mdir);
2065 #ifdef JEFFB /* see my notes */
2066     else {          /* make home dir the default dir */
2067         strcpy(tmp, getenv("HOME"));
2068         if (tmp == NULL)
2069             *tmp = '/';
2070     }
2071 #endif
2072     /* append exactly one / to end of tmp */
2073     length = strlen(tmp);
2074     if ((tmp[length-1] == '/')&&(tmp[length-2] == '/'))
2075 	tmp[length-1] = '\0';
2076     else if (tmp[length-1] != '/')
2077     {	tmp[length] = '/';
2078 	tmp[length+1] = '\0';
2079     }
2080 }
2081 
2082 
2083 /***************************************************************
2084  * give tmp the string to be printed out as the default path, as
2085  * determined by mdir and curfile, which may also be adjusted ***/
2086 void
get_default_path(char * tmp)2087 get_default_path(char *tmp)
2088 {
2089     int i, len_mdir, len_curfile, len_same, length;
2090     char tmp2[PATHLEN];
2091 
2092 #ifdef JEFFB /* see my notes */
2093     if (!(mdir)&&!(*curfile)) 	  /* give tmp the home dir, with 1 / at end */
2094     {	strcpy(tmp, getenv("HOME"));
2095         if (tmp == NULL)
2096             *tmp = '/';
2097         else        /* this is to add a / to end of default dir */
2098         {   length = strlen(tmp);
2099             tmp[length] = '/';
2100             tmp[length +1] = '\0';
2101     	}
2102     }
2103     else
2104 #endif
2105     if((mdir)&&!(*curfile))     /* copy mdir to tmp, with 1 / at end */
2106     {	length = strlen(mdir);
2107         strcpy(tmp, mdir);
2108         if ((tmp[length -1] == '/')&&(tmp[length -2] == '/'))
2109             tmp[length -1] = '\0';
2110 	if (tmp[length -1] != '/')
2111 	{   tmp[length] = '/';
2112 	    tmp[length +1] = '\0';
2113  	}
2114     }
2115     else if(!(mdir)&&(*curfile))  	/* print out most recent curfile */
2116      	strcpy(tmp, curfile);
2117 
2118     else  	/* ((mdir)&&(*curfile)), find best solution */
2119     {	len_mdir = strlen(mdir);
2120         len_curfile = strlen(curfile);
2121 
2122 	if (strcmp(mdir, "/") == 0)
2123 	{   if (*curfile == '/')
2124 		strcpy(tmp, curfile);
2125 	    else		/* tmp = /curfile */
2126 	    {	tmp[0] = '/';
2127 		strcpy(tmp2, curfile);
2128 		for (i = 1; i <= len_curfile; i++)
2129 		    tmp[i] = tmp2[i -1];
2130 	    }
2131 	    return;
2132 	}
2133 	/* find out how many initial chars are the same */
2134 	strcpy(tmp, curfile);
2135 	strcpy(tmp2, mdir);
2136 	for (i = 0; (i < len_mdir)&&(i < len_curfile); i++)
2137 	    if (tmp[i] != tmp2[i])  break;
2138 	len_same = i;
2139 	if (len_same < 3)	/* leave it alone */
2140 	    return;
2141 	else
2142 	{   /* find length of last word in curfile */
2143 	    for (i = len_curfile -1; i > 0; i--)
2144 		if (tmp[i] == '/')	/* strange, no / present */
2145 		    return;
2146 	    /* now copy entire path minus filename to mdir */
2147 	    strcpy(tmp2, tmp); /* note: length of mdir can be shortened, */
2148 	    tmp2[i +1] = '\0'; /* 	but not increased.  This is      */
2149 	    strcpy(mdir, tmp2);/*	still better than nothing.	 */
2150 	}
2151     }
2152 }   /* get_default_path */
2153 #endif /* NOTUSED */
2154 
2155 
2156 /*
2157  * make a backup copy of a file, use the same mode and name in the format
2158  * [path/]#file~
2159  * return 1 if we were successful, 0 otherwise
2160  */
2161 int
backup_file(path)2162 backup_file(path)
2163 char	*path;
2164 {
2165 	struct	stat	statbuf;
2166 	char	fname[PATHLEN];
2167 	char	tpath[PATHLEN];
2168 #if HAVE_ST_BLKSIZE
2169 	static	char	*buf = NULL;
2170 	static	unsigned buflen = 0;
2171 #else
2172 	char	buf[BUFSIZ];
2173 #endif
2174 	char	*tpp;
2175 	int	infd, outfd;
2176 	int	count;
2177 
2178 	/* tpath will be the [path/]file ---> [path/]#file~ */
2179 	strcpy(tpath, path);
2180 	if ((tpp = strrchr(tpath, '/')) == NULL)
2181 		tpp = tpath;
2182 	else
2183 		tpp++;
2184 	strcpy(fname, tpp);
2185 	(void) sprintf(tpp, "#%s~", fname);
2186 
2187 	if (stat(path, &statbuf) == 0)
2188 	{
2189 		/* if we know the optimum block size, use it */
2190 #ifdef HAVE_ST_BLKSIZE
2191 		if ((statbuf.st_blksize > buflen) || (buf == NULL))
2192 		{	buflen = statbuf.st_blksize;
2193 			if ((buf = scxrealloc(buf, buflen)) == (char *)0)
2194 			{	buflen = 0;
2195 				return(0);
2196 			}
2197 		}
2198 #endif
2199 
2200 		if ((infd = open(path, O_RDONLY, 0)) < 0)
2201 			return(0);
2202 
2203 		if ((outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT,
2204 					statbuf.st_mode)) < 0)
2205 			return(0);
2206 
2207 #ifdef HAVE_ST_BLKSIZE
2208 		while((count = read(infd, buf, statbuf.st_blksize)) > 0)
2209 #else
2210 		while((count = read(infd, buf, sizeof(buf))) > 0)
2211 #endif
2212 		{	if (write(outfd, buf, count) != count)
2213 			{	count = -1;
2214 				break;
2215 			}
2216 		}
2217 		close(infd);
2218 		close(outfd);
2219 
2220 		return((count < 0) ? 0 : 1);
2221 	}
2222 	else
2223 	if (errno == ENOENT)
2224 		return(1);
2225 	return(0);
2226 }
2227 #endif /* DOBACKUP */
2228 
2229 #ifdef JEFFB	/* old version */
2230 /* replace a possible '.sc', '.s', or '.' suffix with "ending" */
2231 char *
fsuffix(fname,ending)2232 fsuffix(fname, ending)
2233 char	*fname;
2234 char	*ending;
2235 {	char	*newname, *chp, *slp;
2236 
2237 	if ((newname = scxmalloc(strlen(fname)+1+strlen(ending))) != NULL)
2238 	{
2239 		strcpy(newname, fname);
2240 		/*
2241 		 * chp will point to the start of the filename, path seperator
2242 		 */
2243 		if ((slp = strrchr(newname, '/')) != NULL)
2244 		{	*slp = '\0';
2245 			chp = slp+1;
2246 		}
2247 		else
2248 			chp = newname;
2249 
2250 		/* start of .sc? */
2251 		if ((chp = strrchr(chp, '.')) != NULL)
2252 		{	if (strncmp(chp, ".sc", strlen(chp)) == 0)
2253 				*chp = '\0';
2254 		}
2255 
2256 		/* put the path '/' back */
2257 		if (slp != NULL)
2258 			*slp = '/';
2259 		strcat(newname, ending);
2260 	}
2261 	return(newname);
2262 }
2263 #endif /* JEFFB */
2264 
2265 /*
2266  * if ending !NULL: replace a possible '.sc', '.s', or '.' suffix with "ending"
2267  * if path !NULL: add to front (remove starting '*' from fname)
2268  */
2269 char *
fsuffix(path,fname,ending)2270 fsuffix(path, fname, ending)
2271 char	*path, *fname;
2272 char	*ending;
2273 {	char	*newname, *chp, *slp;
2274 	int	len;
2275 
2276 	len = strlen(fname)+1;
2277 	if (path != NULL)
2278 		len += strlen(path);
2279 	if (ending != NULL)
2280 		len += strlen(ending);
2281 	if ((newname = scxmalloc(len)) != NULL)
2282 	{
2283 		if (path != NULL)	/* remove leading '*', using mdir */
2284 			sprintf(newname, "%s/%s", path, fname+1);
2285 		else
2286 			strcpy(newname, fname);
2287 
2288 		if (ending == NULL)		/* no suffix to replace */
2289 			return(newname);
2290 
2291 		/*
2292 		 * chp will point to the start of the filename, path seperator
2293 		 */
2294 		if ((slp = strrchr(newname, '/')) != NULL)
2295 		{	*slp = '\0';
2296 			chp = slp+1;
2297 		}
2298 		else
2299 			chp = newname;
2300 
2301 		/* start of .sc? */
2302 		if ((chp = strrchr(chp, '.')) != NULL)
2303 		{	if (strncmp(chp, ".sc", strlen(chp)) == 0)
2304 				*chp = '\0';
2305 		}
2306 
2307 		/* put the path '/' back */
2308 		if (slp != NULL)
2309 			*slp = '/';
2310 		strcat(newname, ending);
2311 	}
2312 	return(newname);
2313 }
2314 
2315 
2316 /* used by routines which will accept a >> designation on a filename
2317  * used to specify append to a file
2318  */
2319 static char *
extract_filename(fname)2320 extract_filename(fname)
2321 char	*fname;
2322 {
2323 	/* default mode */
2324 	file_mode = "w";
2325 
2326 	while (isspace(*fname))
2327 		fname++;
2328 	/* "> file" will be interpreted as write, ">> file" as append */
2329 	if ((*fname == '>') && (*++fname == '>'))
2330 	{	file_mode = "a";
2331 		fname++;
2332 	}
2333 
2334 	while (isspace(*fname))
2335 		fname++;
2336 	return fname;
2337 }
2338 
2339 char *
what_file(fname,suffix)2340 what_file(fname, suffix)
2341 char *fname, *suffix;
2342 {
2343     fname = extract_filename(fname);
2344     if (*fname == '*' && mdir)
2345     	fname = fsuffix(mdir, fname, suffix);
2346     else
2347 	fname = fsuffix(NULL, fname, suffix);
2348     return(fname);
2349 }
2350