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