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: 7.16 $
11  */
12 
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #if defined(BSD42) || defined(BSD43)
16 #include <strings.h>
17 #else
18 #ifndef SYSIII
19 #include <string.h>
20 #endif
21 #endif
22 
23 #include <curses.h>
24 #include <time.h>
25 #include <utime.h>
26 #if defined(BSD42) || defined(BSD43) || defined(VMS)
27 #include <sys/file.h>
28 #else
29 #include <fcntl.h>
30 #endif
31 #include "sc.h"
32 #include <signal.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #ifndef MSDOS
36 #include <unistd.h>
37 #endif
38 
39 void	syncref(register struct enode *e);
40 void	unspecial(FILE *f, char *str, int delim);
41 
42 /* a linked list of free [struct ent]'s, uses .next as the pointer */
43 extern	struct ent *freeents;
44 
45 /* a linked list of free [struct enodes]'s, uses .e.o.left as the pointer */
46 extern	struct enode *freeenodes;
47 
48 #define DEFCOLDELIM ':'
49 
50 extern	char *scext;
51 extern	char *ascext;
52 extern	char *tbl0ext;
53 extern	char *tblext;
54 extern	char *latexext;
55 extern	char *slatexext;
56 extern	char *texext;
57 extern	int  Vopt;
58 extern	struct go_save gs;
59 int macrofd;
60 int cslop;
61 struct impexfilt *filt = NULL; /* root of list of impex filters */
62 
63 /* copy the current row (currow) and place the cursor in the new row */
64 void
duprow()65 duprow()
66 {
67     int c1, c2, coltmp = curcol;
68     struct frange *fr;
69 
70     fr = find_frange(currow, curcol);
71     c1 = fr ? fr->or_left->col : 0;
72     c2 = fr ? fr->or_right->col : maxcol;
73 
74     if (currow >= maxrows - 1 || (!fr && maxrow >= maxrows - 1) ||
75 	    (fr && fr->or_right->row >= maxrows - 1)) {
76 	if (!growtbl(GROWROW, 0, 0))
77 		return;
78     }
79     modflg++;
80     insertrow(1, 1);
81     for (curcol = c1; curcol <= c2; curcol++) {
82 	register struct ent *p = *ATBL(tbl, currow - 1, curcol);
83 	if (p) {
84 	    register struct ent *n;
85 	    n = lookat(currow, curcol);
86 	    (void) copyent(n, p, 1, 0, 0, 0, maxrow, maxcol, 0);
87 	}
88     }
89     curcol = coltmp;
90 }
91 
92 /* copy the current column (curcol) and place the cursor in the new column */
93 void
dupcol()94 dupcol()
95 {
96     int rowtmp = currow;
97 
98     if (curcol >= maxcols - 1 || maxcol >= maxcols - 1) {
99 	if (!growtbl(GROWCOL, 0, 0))
100 		return;
101     }
102     modflg++;
103     insertcol(1, 1);
104     for (currow = 0; currow <= maxrow; currow++) {
105 	register struct ent *p = *ATBL(tbl, currow, curcol - 1);
106 	if (p) {
107 	    register struct ent *n;
108 	    n = lookat(currow, curcol);
109 	    copyent(n, p, 0, 1, 0, 0, maxrow, maxcol, 0);
110 	}
111     }
112     currow = rowtmp;
113 }
114 
115 /* Insert 'arg' rows.  The row(s) will be inserted before currow if delta
116  * is 0; after if it is 1.
117  */
118 void
insertrow(int arg,int delta)119 insertrow(int arg, int delta)
120 {
121     int	r, c, i;
122     struct ent	**tmprow, **pp;
123     int lim = maxrow - currow + 1;
124     struct frange *fr;
125 
126     if (currow > maxrow)
127 	maxrow = currow;
128     maxrow += arg;
129     lim = maxrow - lim + delta;
130 
131     if ((maxrow >= maxrows) && !growtbl(GROWROW, maxrow, 0))
132 	return;
133 
134     if ((fr = find_frange(currow + delta, curcol))) {
135 	move_area(currow + arg + delta, fr->or_left->col, currow + delta,
136 		fr->or_left->col, fr->or_right->row, fr->or_right->col);
137 	if (!delta && fr->ir_left->row == currow + arg)
138 	    fr->ir_left = lookat(fr->ir_left->row - arg, fr->ir_left->col);
139 	if (delta && fr->ir_right->row == currow)
140 	    fr->ir_right = lookat(fr->ir_right->row + arg, fr->ir_right->col);
141 
142 	for (i = 0; i < 37; i++) {	/* update all marked cells */
143 	    if (savedrow[i] >= currow + delta &&
144 		    savedcol[i] >= fr->or_left->col &&
145 		    savedcol[i] <= fr->or_right->col)
146 		savedrow[i] += arg;
147 	    if (savedstrow[i] >= currow + delta &&
148 		    savedstcol[i] >= fr->or_left->col &&
149 		    savedstcol[i] <= fr->or_right->col)
150 		savedstrow[i] += arg;
151 	}
152 	if (gs.g_row >= currow + delta &&
153 		gs.g_col >= fr->or_left->col &&
154 		gs.g_col <= fr->or_right->col)
155 	    gs.g_row += arg;
156 	if (gs.g_lastrow >= currow + delta &&
157 		gs.g_lastcol >= fr->or_left->col &&
158 		gs.g_lastcol <= fr->or_right->col)
159 	    gs.g_lastrow += arg;
160 	if (gs.strow >= currow + delta &&
161 		gs.stcol >= fr->or_left->col &&
162 		gs.stcol <= fr->or_right->col)
163 	    gs.strow += arg;
164 	for (r = 0; r <= maxrow; r++) {
165 	    for (c = 0; c <= maxcol; c++) {
166 		pp = ATBL(tbl, r, c);
167 		if (*pp) {
168 		    if ((*pp)->nrow >= currow + delta &&
169 			    (*pp)->ncol >= fr->or_left->col &&
170 			    (*pp)->ncol <= fr->or_right->col)
171 			((*pp)->nrow) += arg;
172 		    if ((*pp)->nlastrow >= currow + delta &&
173 			    (*pp)->nlastcol >= fr->or_left->col &&
174 			    (*pp)->nlastcol <= fr->or_right->col)
175 			((*pp)->nlastrow) += arg;
176 		}
177 	    }
178 	}
179     } else {
180 
181 	/*
182 	 * save the last active row+1, shift the rows downward, put the last
183 	 * row in place of the first
184 	 */
185 	tmprow = tbl[maxrow];
186 	for (r = maxrow; r > lim; r--) {
187 	    row_hidden[r] = row_hidden[r-arg];
188 	    row_hidden[r-arg] = row_hidden[r-1];
189 	    tbl[r] = tbl[r-arg];
190 	    tbl[r-arg] = tbl[r-1];
191 	    pp = ATBL(tbl, r, 0);
192 	    for (c = 0; c < maxcols; c++, pp++)
193 		if (*pp)
194 		    (*pp)->row = r;
195 	}
196 	tbl[r] = tmprow;		/* the last row was never used.... */
197 
198 	for (i = 0; i < 37; i++) {	/* update all marked cells */
199 	    if (savedrow[i] >= currow + delta)
200 		savedrow[i] += arg;
201 	    if (savedstrow[i] >= currow + delta)
202 		savedstrow[i] += arg;
203 	}
204 	if (gs.g_row >= currow + delta)
205 	    gs.g_row += arg;
206 	if (gs.g_lastrow >= currow + delta)
207 	    gs.g_lastrow += arg;
208 	if (gs.strow >= currow + delta)
209 	    gs.strow += arg;
210 	for (r = 0; r <= maxrow; r++) {
211 	    for (c = 0; c <= maxcol; c++) {
212 		pp = ATBL(tbl, r, c);
213 		if (*pp) {
214 		    if ((*pp)->nrow >= currow + delta)
215 			((*pp)->nrow) += arg;
216 		    if ((*pp)->nlastrow >= currow + delta)
217 			((*pp)->nlastrow) += arg;
218 		}
219 	    }
220 	}
221     }
222     fix_ranges(currow + arg * (1 - delta), -1, currow + arg * (1 - delta), -1,
223 	    delta?0:arg, delta?arg:0);
224     currow += delta;
225     FullUpdate++;
226     modflg++;
227 }
228 
229 /* Insert 'arg' cols.  The col(s) will be inserted before curcol if delta
230  * is 0; after if it is 1.
231  */
232 void
insertcol(int arg,int delta)233 insertcol(int arg, int delta)
234 {
235     int r, c;
236     register struct ent **pp;
237     int lim = maxcol - curcol - delta + 1;
238     struct frange *fr;
239 
240     if (curcol + delta > maxcol)
241 	maxcol = curcol + delta;
242     maxcol += arg;
243 
244     if ((maxcol >= maxcols) && !growtbl(GROWCOL, 0, maxcol))
245 	return;
246 
247     for (c = maxcol; c >= curcol + delta + arg; c--) {
248 	fwidth[c] = fwidth[c-arg];
249 	precision[c] = precision[c-arg];
250 	realfmt[c] = realfmt[c-arg];
251 	col_hidden[c] = col_hidden[c-arg];
252     }
253     for (c = curcol + delta; c - curcol - delta < arg; c++) {
254     	fwidth[c] = DEFWIDTH;
255 	precision[c] =  DEFPREC;
256 	realfmt[c] = DEFREFMT;
257 	col_hidden[c] = FALSE;
258     }
259 
260     for (r=0; r <= maxrow; r++) {
261 	pp = ATBL(tbl, r, maxcol);
262 	for (c = lim; --c >= 0; pp--)
263 	    if ((pp[0] = pp[-arg]))
264 		pp[0]->col += arg;
265 
266 	pp = ATBL(tbl, r, curcol + delta);
267 	for (c = curcol + delta; c - curcol - delta < arg; c++, pp++)
268 	    *pp = (struct ent *)0;
269     }
270 
271     /* Update all marked cells. */
272     for (c=0; c < 37; c++) {
273 	if (savedcol[c] >= curcol + delta)
274 	    savedcol[c] += arg;
275 	if (savedstcol[c] >= curcol + delta)
276 	    savedstcol[c] += arg;
277     }
278     if (gs.g_col >= curcol + delta)
279 	gs.g_col += arg;
280     if (gs.g_lastcol >= curcol + delta)
281 	gs.g_lastcol += arg;
282     if (gs.stcol >= curcol + delta)
283 	gs.stcol += arg;
284 
285     /* Update note links. */
286     for (r = 0; r <= maxrow; r++) {
287 	for (c = 0; c <= maxcol; c++) {
288 	    pp = ATBL(tbl, r, c);
289 	    if (*pp) {
290 		if ((*pp)->ncol >= curcol + delta)
291 		    ((*pp)->ncol) += arg;
292 		if ((*pp)->nlastcol >= curcol + delta)
293 		    ((*pp)->nlastcol) += arg;
294 	    }
295 	}
296     }
297     if (!delta && (fr = find_frange(currow, curcol)) &&
298 	    fr->ir_left->col == curcol + arg)
299 	fr->ir_left = lookat(fr->ir_left->row, fr->ir_left->col - arg);
300     if (delta && (fr = find_frange(currow, curcol)) &&
301 	    fr->ir_right->col == curcol)
302 	fr->ir_right = lookat(fr->ir_right->row, fr->ir_right->col + arg);
303     fix_ranges(-1, curcol + arg * (1 - delta), -1, curcol + arg * (1 - delta),
304 	    delta?0:arg, delta?arg:0);
305     curcol += delta;
306     FullUpdate++;
307     modflg++;
308 }
309 
310 /* delete 'arg' rows starting at currow (deletes from currow downward) */
311 void
deleterow(register int arg)312 deleterow(register int arg)
313 {
314     int i;
315     int rs = maxrow - currow + 1;
316     struct frange *fr;
317     struct ent *p;
318     struct ent *obuf = NULL;
319     char buf[50];
320 
321     if ((fr = find_frange(currow, curcol)))
322 	rs = fr->or_right->row - currow + 1;
323     if (rs - arg < 0) {
324 	rs = rs > 0 ? rs : 0;
325 	(void) sprintf(buf, "Can't delete %d row%s %d row%s left", arg,
326 		(arg != 1 ? "s," : ","), rs, (rs != 1 ? "s" : ""));
327 	error(buf);
328 	return;
329     }
330     if (fr) {
331 	if (any_locked_cells(currow, fr->or_left->col,
332 		currow + arg - 1, fr->or_right->col))
333 	    error("Locked cells encountered. Nothing changed");
334 	else {
335 	    FullUpdate++;
336 	    modflg++;
337 	    if (dbidx < 0) dbidx++;
338 	    delbuf[dbidx] = delbuf[DELBUFSIZE - 1];
339 	    delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 1];
340 	    delbuf[DELBUFSIZE - 1] = NULL;
341 	    delbuffmt[DELBUFSIZE - 1] = NULL;
342 	    for (i = dbidx + 1; i < DELBUFSIZE; i++) {
343 		if (delbuf[i] == delbuf[dbidx]) {
344 		    delbuf[dbidx] = NULL;
345 		    delbuffmt[dbidx] = NULL;
346 		    break;
347 		}
348 	    }
349 	    flush_saved();
350 	    if (qbuf) {
351 		if (dbidx < 0) dbidx++;
352 		delbuf[dbidx] = delbuf[qbuf];
353 		delbuffmt[dbidx] = delbuffmt[qbuf];
354 		flush_saved();
355 		obuf = delbuf[qbuf];	/* orig. contents of the del. buffer */
356 	    }
357 	    sync_refs();
358 	    erase_area(currow, fr->or_left->col, currow + arg - 1,
359 		    fr->or_right->col, 0);
360 	    fix_ranges(currow, -1, currow + arg - 1, -1, -1, -1);
361 	    for (i = 0; i < DELBUFSIZE; i++)
362 		if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) {
363 		    delbuf[i] = delbuf[dbidx];
364 		    delbuffmt[i] = delbuffmt[dbidx];
365 		}
366 	    qbuf = 0;
367 	    for (i = DELBUFSIZE - 1; i > DELBUFSIZE - 9; i--) {
368 		delbuf[i] = delbuf[i-1];
369 		delbuffmt[i] = delbuffmt[i-1];
370 	    }
371 	    delbuf[DELBUFSIZE - 9] = delbuf[dbidx];
372 	    delbuffmt[DELBUFSIZE - 9] = delbuffmt[dbidx];
373 	    for (p = delbuf[dbidx]; p; p = p->next)
374 		p->flags &= ~may_sync;
375 	    if (currow + arg > fr->ir_right->row &&
376 		    fr->ir_right->row >= currow)
377 		fr->ir_right = lookat(currow - 1, fr->ir_right->col);
378 	    if (currow + arg > fr->or_right->row)
379 		fr->or_right = lookat(currow - 1, fr->or_right->col);
380 	    else
381 		move_area(currow, fr->or_left->col, currow + arg,
382 			fr->or_left->col, fr->or_right->row, fr->or_right->col);
383 	    if (fr->ir_left->row > fr->ir_right->row)
384 		add_frange(fr->or_left, fr->or_right, NULL, NULL, 0, 0, 0, 0);
385 
386 	    /* Update all marked cells. */
387 	    for (i = 0; i < 37; i++) {
388 		if (savedcol[i] >= fr->or_left->col &&
389 			savedcol[i] <= fr->or_right->col) {
390 		    if (savedrow[i] >= currow && savedrow[i] < currow + arg)
391 			savedrow[i] = savedcol[i] = -1;
392 		    if (savedrow[i] >= currow + arg)
393 			savedrow[i] -= arg;
394 		}
395 		if (savedstcol[i] >= fr->or_left->col &&
396 			savedstcol[i] <= fr->or_right->col) {
397 		    if (savedstrow[i] >= currow && savedstrow[i] < currow + arg)
398 			savedstrow[i] = currow;
399 		    if (savedstrow[i] >= currow + arg)
400 			savedstrow[i] -= arg;
401 		}
402 	    }
403 	    if (gs.g_col >= fr->or_left->col &&
404 		    gs.g_col <= fr->or_right->col) {
405 		if (gs.g_row >= currow && gs.g_row < currow + arg)
406 		    gs.g_row = currow;
407 		if (gs.g_row >= currow + arg)
408 		    gs.g_row -= arg;
409 	    }
410 	    if (gs.g_lastcol >= fr->or_left->col &&
411 		    gs.g_lastcol <= fr->or_right->col) {
412 		if (gs.g_lastrow >= currow && gs.g_lastrow < currow + arg)
413 		    gs.g_lastrow = currow - 1;
414 		if (gs.g_lastrow >= currow + arg)
415 		    gs.g_lastrow -= arg;
416 	    }
417 	    if (gs.g_row > gs.g_lastrow)
418 		gs.g_row = gs.g_col = -1;
419 	    if (gs.stcol >= fr->or_left->col &&
420 		    gs.stcol <= fr->or_right->col) {
421 		if (gs.strow >= currow && gs.strow < currow + arg)
422 		    gs.strow = currow;
423 		if (gs.strow >= currow + arg)
424 		    gs.strow -= arg;
425 	    }
426 	}
427     } else {
428 
429 	if (any_locked_cells(currow, 0, currow + arg - 1, maxcol))
430 	    error("Locked cells encountered. Nothing changed");
431 	else {
432 	    if (dbidx < 0) dbidx++;
433 	    delbuf[dbidx] = delbuf[DELBUFSIZE - 1];
434 	    delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 1];
435 	    delbuf[DELBUFSIZE - 1] = NULL;
436 	    delbuffmt[DELBUFSIZE - 1] = NULL;
437 	    for (i = dbidx + 1; i < DELBUFSIZE; i++) {
438 		if (delbuf[i] == delbuf[dbidx]) {
439 		    delbuf[dbidx] = NULL;
440 		    delbuffmt[dbidx] = NULL;
441 		    break;
442 		}
443 	    }
444 	    flush_saved();
445 	    if (qbuf) {
446 		if (dbidx < 0) dbidx++;
447 		delbuf[dbidx] = delbuf[qbuf];
448 		delbuffmt[dbidx] = delbuffmt[qbuf];
449 		flush_saved();
450 		obuf = delbuf[qbuf];	/* orig. contents of the del. buffer */
451 	    }
452 	    sync_refs();
453 	    erase_area(currow, 0, currow + arg - 1, maxcol, 0);
454 	    fix_ranges(currow, -1, currow + arg - 1, -1, -1, -1);
455 	    closerow(currow, arg);
456 	    for (i = 0; i < DELBUFSIZE; i++)
457 		if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) {
458 		    delbuf[i] = delbuf[dbidx];
459 		    delbuffmt[i] = delbuffmt[dbidx];
460 		}
461 	    qbuf = 0;
462 	    for (i = DELBUFSIZE - 1; i > DELBUFSIZE - 9; i--) {
463 		delbuf[i] = delbuf[i-1];
464 		delbuffmt[i] = delbuffmt[i-1];
465 	    }
466 	    delbuf[DELBUFSIZE - 9] = delbuf[dbidx];
467 	    delbuffmt[DELBUFSIZE - 9] = delbuffmt[dbidx];
468 	    for (p = delbuf[dbidx]; p; p = p->next)
469 		p->flags &= ~may_sync;
470 	}
471     }
472 }
473 
474 void
yankrow(int arg)475 yankrow(int arg)
476 {
477     int rs = maxrow - currow + 1;
478     int i, qtmp;
479     char buf[50];
480     struct frange *fr;
481     struct ent *obuf;
482 
483     if ((fr = find_frange(currow, curcol)))
484 	rs = fr->or_right->row - currow + 1;
485     if (rs - arg < 0) {
486 	rs = rs > 0 ? rs : 0;
487 	(void) sprintf(buf, "Can't yank %d row%s %d row%s left", arg,
488 		(arg != 1 ? "s," : ","), rs, (rs != 1 ? "s" : ""));
489 	error(buf);
490 	return;
491     }
492     sync_refs();
493     if (dbidx < 0) dbidx++;
494     delbuf[dbidx] = delbuf[DELBUFSIZE - 10];
495     delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 10];
496     delbuf[DELBUFSIZE - 10] = NULL;
497     delbuffmt[DELBUFSIZE - 10] = NULL;
498     for (i = dbidx + 1; i < DELBUFSIZE; i++) {
499 	if (delbuf[i] == delbuf[dbidx]) {
500 	    delbuf[dbidx] = NULL;
501 	    delbuffmt[dbidx] = NULL;
502 	    break;
503 	}
504     }
505     flush_saved();
506     if (qbuf) {
507 	if (dbidx < 0) dbidx++;
508 	delbuf[dbidx] = delbuf[qbuf];
509 	delbuffmt[dbidx] = delbuffmt[qbuf];
510 	flush_saved();
511 	obuf = delbuf[qbuf];	/* orig. contents of the del. buffer */
512     }
513     qtmp = qbuf;
514     qbuf = 0;
515     if (fr) {
516 	yank_area(currow, fr->or_left->col, currow + arg - 1,
517 		fr->or_right->col);
518     } else {
519 	yank_area(currow, 0, currow + arg - 1, maxcol);
520     }
521     qbuf = qtmp;
522     for (i = 0; i < DELBUFSIZE; i++)
523 	if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) {
524 	    delbuf[i] = delbuf[dbidx];
525 	    delbuffmt[i] = delbuffmt[dbidx];
526 	}
527     qbuf = 0;
528     delbuf[DELBUFSIZE - 10] = delbuf[dbidx];
529     delbuffmt[DELBUFSIZE - 10] = delbuffmt[dbidx];
530 }
531 
532 void
yankcol(int arg)533 yankcol(int arg)
534 {
535     int cs = maxcol - curcol + 1;
536     int i, qtmp;
537     char buf[50];
538     struct ent *obuf;
539 
540     if (cs - arg < 0) {
541     	cs = cs > 0 ? cs : 0;
542 	(void) sprintf(buf, "Can't yank %d column%s %d column%s left", arg,
543 		(arg != 1 ? "s," : ","), cs, (cs != 1 ? "s" : ""));
544 	error(buf);
545 	return;
546     }
547     sync_refs();
548     if (dbidx < 0) dbidx++;
549     delbuf[dbidx] = delbuf[DELBUFSIZE - 10];
550     delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 10];
551     delbuf[DELBUFSIZE - 10] = NULL;
552     delbuffmt[DELBUFSIZE - 10] = NULL;
553     for (i = dbidx + 1; i < DELBUFSIZE; i++) {
554 	if (delbuf[i] == delbuf[dbidx]) {
555 	    delbuf[dbidx] = NULL;
556 	    delbuffmt[dbidx] = NULL;
557 	    break;
558 	}
559     }
560     flush_saved();
561     if (qbuf) {
562 	if (dbidx < 0) dbidx++;
563 	delbuf[dbidx] = delbuf[qbuf];
564 	delbuffmt[dbidx] = delbuffmt[qbuf];
565 	flush_saved();
566 	obuf = delbuf[qbuf];	/* orig. contents of the del. buffer */
567     }
568     qtmp = qbuf;
569     qbuf = 0;
570     yank_area(0, curcol, maxrow, curcol + arg - 1);
571     qbuf = qtmp;
572     for (i = 0; i < DELBUFSIZE; i++)
573 	if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) {
574 	    delbuf[i] = delbuf[dbidx];
575 	    delbuffmt[i] = delbuffmt[dbidx];
576 	}
577     qbuf = 0;
578     delbuf[DELBUFSIZE - 10] = delbuf[dbidx];
579     delbuffmt[DELBUFSIZE - 10] = delbuffmt[dbidx];
580 }
581 
582 /* ignorelock is used when sorting so that locked cells can still be sorted */
583 
584 void
erase_area(int sr,int sc,int er,int ec,int ignorelock)585 erase_area(int sr, int sc, int er, int ec, int ignorelock)
586 {
587     int r, c;
588     struct ent **pp;
589 
590     if (sr > er) {
591 	r = sr; sr = er; er = r;
592     }
593 
594     if (sc > ec) {
595 	c = sc; sc = ec; ec = c;
596     }
597 
598     if (sr < 0)
599 	sr = 0;
600     if (sc < 0)
601 	sc = 0;
602     checkbounds(&er, &ec);
603 
604     /* Do a lookat() for the upper left and lower right cells of the range
605      * being erased to make sure they are included in the delete buffer so
606      * that pulling cells always works correctly even if the cells at one
607      * or more edges of the range are all empty.
608      */
609     (void) lookat(sr, sc);
610     (void) lookat(er, ec);
611 
612     delbuffmt[++dbidx] = (char*)scxmalloc((4*(ec-sc+1)+(er-sr+1))*sizeof(char));
613     for (c = sc; c <= ec; c++) {
614 	delbuffmt[dbidx][4*(c-sc)] = (char)fwidth[c];
615 	delbuffmt[dbidx][4*(c-sc)+1] = (char)precision[c];
616 	delbuffmt[dbidx][4*(c-sc)+2] = (char)realfmt[c];
617 	delbuffmt[dbidx][4*(c-sc)+3] = (char)col_hidden[c];
618     }
619     for (r = sr; r <= er; r++) {
620 	for (c = sc; c <= ec; c++) {
621 	    pp = ATBL(tbl, r, c);
622 	    if (*pp && (!((*pp)->flags&is_locked) || ignorelock)) {
623 		free_ent(*pp, 0);
624 		*pp = NULL;
625 	    }
626 	}
627 	delbuffmt[dbidx][4*(ec-sc+1)+(r-sr)] = (char)row_hidden[r];
628     }
629 }
630 
631 void
yank_area(int sr,int sc,int er,int ec)632 yank_area(int sr, int sc, int er, int ec)
633 {
634     int r, c;
635 
636     if (sr > er) {
637 	r = sr; sr = er; er = r;
638     }
639 
640     if (sc > ec) {
641 	c = sc; sc = ec; ec = c;
642     }
643 
644     if (sr < 0)
645 	sr = 0;
646     if (sc < 0)
647 	sc = 0;
648     checkbounds(&er, &ec);
649 
650     r = currow;
651     currow = sr;
652     c = curcol;
653     curcol = sc;
654 
655     erase_area(sr, sc, er, ec, 0);
656     pullcells('p');
657 
658     currow = r;
659     curcol = c;
660 }
661 
662 void
move_area(int dr,int dc,int sr,int sc,int er,int ec)663 move_area(int dr, int dc, int sr, int sc, int er, int ec)
664 {
665     struct ent *p;
666     struct ent **pp;
667     int deltar, deltac;
668     int r, c;
669 
670     if (sr > er) {
671 	r = sr; sr = er; er = r;
672     }
673 
674     if (sc > ec) {
675 	c = sc; sc = ec; ec = c;
676     }
677 
678     if (sr < 0)
679 	sr = 0;
680     if (sc < 0)
681 	sc = 0;
682     checkbounds(&er, &ec);
683 
684     r = currow;
685     currow = sr;
686     c = curcol;
687     curcol = sc;
688 
689     /* First we erase the source range, which puts the cells on the delete
690      * buffer stack.
691      */
692     erase_area(sr, sc, er, ec, 0);
693 
694     currow = r;
695     curcol = c;
696     deltar = dr - sr;
697     deltac = dc - sc;
698 
699     /* Now we erase the destination range, which adds it to the delete buffer
700      * stack, but then we flush it off.  We then move the original source
701      * range from the stack to the destination range, adjusting the addresses
702      * as we go, leaving the stack in its original state.
703      */
704     erase_area(dr, dc, er + deltar, ec + deltac, 0);
705     flush_saved();
706     for (p = delbuf[dbidx]; p; p = p->next) {
707 	pp = ATBL(tbl, p->row + deltar, p->col + deltac);
708 	*pp = p;
709 	p->row += deltar;
710 	p->col += deltac;
711 	p->flags &= ~is_deleted;
712     }
713     delbuf[dbidx] = NULL;
714     delbuffmt[dbidx--] = NULL;
715 }
716 
717 /*
718  * deletes the expression associated w/ a cell and turns it into a constant
719  * containing whatever was on the screen
720  */
721 void
valueize_area(int sr,int sc,int er,int ec)722 valueize_area(int sr, int sc, int er, int ec)
723 {
724     int r, c;
725     struct ent *p;
726 
727     if (sr > er) {
728 	r = sr; sr = er; er= r;
729     }
730 
731     if (sc > ec) {
732 	c = sc; sc = ec; ec= c;
733     }
734 
735     if (sr < 0)
736 	sr = 0;
737     if (sc < 0)
738 	sc = 0;
739     checkbounds(&er, &ec);
740 
741     for (r = sr; r <= er; r++) {
742 	for (c = sc; c <= ec; c++) {
743 	    p = *ATBL(tbl, r, c);
744 	    if (p && p->flags&is_locked) {
745 		error(" Cell %s%d is locked", coltoa(c), r);
746 		continue;
747 	    }
748 	    if (p && p->expr) {
749 		efree(p->expr);
750 		p->expr = (struct enode *)0;
751 		p->flags &= ~is_strexpr;
752 	    }
753 	}
754     }
755 }
756 
757 void
pullcells(int to_insert)758 pullcells(int to_insert)
759 {
760     struct ent *obuf;
761     struct ent *p, *n;
762     struct ent **pp;
763     int deltar, deltac;
764     int minrow, mincol;
765     int mxrow, mxcol;
766     int numrows, numcols;
767     int i;
768     struct frange *fr;
769 
770     if (qbuf && delbuf[qbuf]) {
771 	delbuf[++dbidx] = delbuf[qbuf];
772 	delbuffmt[dbidx] = delbuffmt[qbuf];
773     }
774     obuf = delbuf[dbidx];	/* orig. contents of the del. buffer */
775 
776     if ((qbuf && !delbuf[qbuf]) || dbidx < 0) {
777 	error("No data to pull");
778 	qbuf = 0;
779 	return;
780     }
781 
782     minrow = maxrows;
783     mincol = maxcols;
784     mxrow = 0;
785     mxcol = 0;
786 
787     for (p = delbuf[dbidx]; p; p = p->next) {
788 	if (p->row < minrow)
789 	    minrow = p->row;
790 	if (p->row > mxrow)
791 	    mxrow = p->row;
792 	if (p->col < mincol)
793 	    mincol = p->col;
794 	if (p->col > mxcol)
795 	    mxcol = p->col;
796 	p->flags |= may_sync;
797     }
798 
799     numrows = mxrow - minrow + 1;
800     numcols = mxcol - mincol + 1;
801     deltar = currow - minrow;
802     deltac = curcol - mincol;
803 
804     if (to_insert == 'C') {
805 	minrow = 0;
806 	mincol = 0;
807 	mxrow = maxrows;
808 	mxcol = maxcols;
809     }
810 
811     if (to_insert == 'r') {
812 	insertrow(numrows, 0);
813 	if (fr = find_frange(currow, curcol))
814 	    deltac = fr->or_left->col - mincol;
815 	else {
816 	    for (i = 0; i < numrows; i++)
817 		row_hidden[currow+i] = delbuffmt[dbidx][4*numcols+i];
818 	    deltac = 0;
819 	}
820     } else if (to_insert == 'c') {
821 	insertcol(numcols, 0);
822 	for (i = 0; i < numcols; i++) {
823 	    fwidth[curcol+i] = delbuffmt[dbidx][4*i];
824 	    precision[curcol+i] = delbuffmt[dbidx][4*i+1];
825 	    realfmt[curcol+i] = delbuffmt[dbidx][4*i+2];
826 	    col_hidden[curcol+i] = delbuffmt[dbidx][4*i+3];
827 	}
828 	deltar = 0;
829     } else if (to_insert == 'x') {	/* Do an exchange. */
830 	struct ent *tmpbuf;
831 	char *tmpfmt;
832 
833 	/* Save the original contents of the destination range on the
834 	 * delete buffer stack in preparation for the exchange, then swap
835 	 * the top two pointers on the stack, so that the original cells
836 	 * to be pulled are still on top.
837 	 */
838 	erase_area(minrow + deltar, mincol + deltac, mxrow + deltar,
839 		mxcol + deltac, 0);
840 	tmpbuf = delbuf[dbidx];
841 	delbuf[dbidx] = delbuf[dbidx - 1];
842 	delbuf[dbidx - 1] = tmpbuf;
843 	tmpfmt = delbuffmt[dbidx];
844 	delbuffmt[dbidx] = delbuffmt[dbidx - 1];
845 	delbuffmt[dbidx - 1] = tmpfmt;
846     } else if (to_insert == 'p') {
847 	erase_area(minrow + deltar, mincol + deltac, mxrow + deltar,
848 		mxcol + deltac, 0);
849 	sync_refs();
850 	flush_saved();
851     } else if (to_insert == 't') {
852 	erase_area(minrow + deltar, mincol + deltac,
853 		minrow + deltar + mxcol - mincol,
854 		mincol + deltac + mxrow - minrow, 0);
855 	sync_refs();
856 	flush_saved();
857     }
858 
859     FullUpdate++;
860     modflg++;
861 
862     /* At this point, we copy the cells from the delete buffer into the
863      * destination range.
864      */
865     for (p = delbuf[dbidx]; p; p = p->next) {
866 	if (to_insert == 't')	/* Transpose rows and columns while pulling. */
867 	    n = lookat(minrow + deltar + p->col - mincol,
868 		    mincol + deltac + p->row - minrow);
869 	else
870 	    n = lookat(p->row + deltar, p->col + deltac);
871 	copyent(n, p, deltar, deltac, minrow, mincol, mxrow, mxcol, to_insert);
872     }
873 
874     /* Now exchange them so that the original cells from the delete buffer
875      * are in the destination range instead of the copies.  When doing a
876      * "pull exchange" ("px" or "pullxchg"), exchange the original contents
877      * of the destination range with the contents of the delete buffer
878      * instead.  Don't do this if transposing or merging (including merging
879      * cell formats), or if the expressions in the destination cells have
880      * been adjusted during a copy.
881      */
882     if (to_insert != 't' && to_insert != 'm' && to_insert != 'f' &&
883 	    to_insert != 'C') {
884 	if (to_insert == 'x') {
885 	    struct ent *tmpbuf = delbuf[dbidx];
886 	    char *tmpfmt = delbuffmt[dbidx];
887 
888 	    delbuf[dbidx] = delbuf[dbidx - 1];
889 	    delbuf[dbidx - 1] = tmpbuf;
890 	    delbuffmt[dbidx] = delbuffmt[dbidx - 1];
891 	    delbuffmt[dbidx - 1] = tmpfmt;
892 	} else
893 	    for (p = delbuf[dbidx++]; p; p = p->next) {
894 		pp = ATBL(tbl, p->row + deltar, p->col + deltac);
895 		if (*pp && !((*pp)->flags & is_locked)) {
896 		    free_ent(*pp, 1);
897 		    *pp = NULL;
898 		}
899 	    }
900 	for (p = delbuf[dbidx - 1]; p; p = p->next) {
901 	    pp = ATBL(tbl, p->row + deltar, p->col + deltac);
902 	    *pp = p;
903 	    p->row += deltar;
904 	    p->col += deltac;
905 	    p->flags &= ~is_deleted;
906 	}
907 	delbuf[dbidx - 1] = delbuf[dbidx];
908 	delbuf[dbidx--] = NULL;
909 
910 	sync_refs();
911 	/*
912 	 * Now change the cell addresses in the delete buffer to match
913 	 * where the original cells came from.
914 	 */
915 	for (p = delbuf[dbidx]; p; p = p->next) {
916 	    p->row -= deltar;
917 	    p->col -= deltac;
918 	}
919     } else
920 	sync_refs();
921 
922     /* Now make sure all references to the pulled cells in all named buffers
923      * point to the new set of cells in the delete buffer.
924      */
925     for (i = 0; i < DELBUFSIZE; i++)
926 	if (delbuf[i] == obuf) {
927 	    delbuf[i] = delbuf[dbidx];
928 	    delbuffmt[i] = delbuffmt[dbidx];
929 	}
930     if (qbuf && delbuf[qbuf]) {
931 	delbuf[dbidx] = NULL;
932 	delbuffmt[dbidx--] = NULL;
933     }
934     qbuf = 0;
935 }
936 
937 void
colshow_op()938 colshow_op()
939 {
940     register int i,j;
941     for (i = 0; i < maxcols; i++)
942 	if (col_hidden[i])
943 	    break;
944     for(j = i; j < maxcols; j++)
945 	if (!col_hidden[j])
946 	    break;
947     j--;
948     if (i >= maxcols)
949 	error("No hidden columns to show");
950     else {
951 	(void) sprintf(line,"show %s:", coltoa(i));
952 	(void) sprintf(line + strlen(line),"%s",coltoa(j));
953 	linelim = strlen(line);
954     }
955 }
956 
957 void
rowshow_op()958 rowshow_op()
959 {
960     register int i,j;
961     for (i = 0; i < maxrows; i++)
962 	if (row_hidden[i])
963 	    break;
964     for(j = i; j < maxrows; j++)
965 	if (!row_hidden[j]) {
966 	    break;
967 	}
968     j--;
969 
970     if (i >= maxrows)
971 	error("No hidden rows to show");
972     else {
973 	(void)sprintf(line,"show %d:%d", i, j);
974         linelim = strlen(line);
975     }
976 }
977 
978 /*
979  * Given a row/column command letter, emit a small menu, then read a qualifier
980  * character for a row/column command and convert it to 'r' (row), 'c'
981  * (column), or 0 (unknown).  If ch is 'p', three extra qualifiers, 'm', 'x',
982  * and 't', are allowed.  If ch is 'Z', an extra qualifier 'Z' is allowed.
983  */
984 
985 int
get_rcqual(int ch)986 get_rcqual(int ch)
987 {
988     int c;
989 
990     error("%sow/column:  r: row  c: column%s",
991 
992 #ifdef KEY_IC
993 	(ch == KEY_IC)		? "Insert r" :
994 #endif
995 	(ch == 'i')		? "Insert r" :
996 	(ch == 'o')		? "Open r" :
997 	(ch == 'a')		? "Append r" :
998 	(ch == 'd')		? "Delete r" :
999 	(ch == 'y')		? "Yank r" :
1000 	(ch == 'p')		? "Pull r" :
1001 	(ch == 'v')		? "Values r" :
1002 	(ch == 'Z')		? "Zap r" :
1003 	(ch == 's')		? "Show r" : "R",
1004 
1005 	(ch == 'p')		? "  p: paste  m: merge  x: xchg  <MORE>" :
1006 	(ch == 'Z')		? "  Z: save/exit" : "");
1007 
1008     (void) refresh();
1009 
1010     switch (c = nmgetch())
1011     {
1012 	case 'r':	return ('r');
1013 
1014 	case 'c':	return ('c');
1015 
1016 	case 'p':	return ((ch == 'p') ? 'p' : 0);
1017 
1018 	case 'm':	return ((ch == 'p') ? 'm' : 0);
1019 
1020 	case 'x':	return ((ch == 'p') ? 'x' : 0);
1021 
1022 	case 't':	return ((ch == 'p') ? 't' : 0);
1023 
1024 	case 'f':	return ((ch == 'p') ? 'f' : 0);
1025 
1026 	case 'C':	return ((ch == 'p') ? 'C' : 0);
1027 
1028 	case '.':	return ((ch == 'p') ? '.' : 0);
1029 
1030 	case 'Z':	return ((ch == 'Z') ? 'Z' : 0);
1031 
1032 	case ESC:
1033 	case ctl('g'):	return (ESC);
1034 
1035 	case 'd':	if (ch == 'd') {
1036 			    ungetch('x');
1037 			    return (ESC);
1038 			} else
1039 			    return (0);
1040 
1041 	case 'y':	if (ch == 'y') {
1042 			    yankr(lookat(currow, curcol),
1043 				    lookat(currow, curcol));
1044 			    return (ESC);
1045 			} else
1046 			    return (0);
1047 
1048 	case 'v':	if (ch == 'v') {
1049 			    valueize_area(currow, curcol, currow, curcol);
1050 			    modflg++;
1051 			    return (ESC);
1052 			} else
1053 			    return (0);
1054 
1055 	case KEY_UP:
1056 	case KEY_DOWN:
1057 	case KEY_PPAGE:
1058 	case KEY_NPAGE:
1059 	case 'j':
1060 	case 'k':
1061 	case 'J':
1062 	case 'K':
1063 	case ctl('f'):
1064 	case ctl('b'):
1065 	case ctl('n'):
1066 	case ctl('p'):	if (ch == 'd')
1067 			    (void) sprintf(line,"deleterow [range] ");
1068 			else if (ch == 'y')
1069 			    (void) sprintf(line,"yankrow [range] ");
1070 			else if (ch == 'Z')
1071 			    (void) sprintf(line,"hide [range] ");
1072 			else
1073 			    return (0);
1074 			edit_mode();
1075 			write_line('A');
1076 			startshow();
1077 			showrange = SHOWROWS;
1078 			showsr = currow;
1079 			ungetch(c);
1080 			return (ESC);
1081 
1082 	case KEY_BACKSPACE:
1083 	case KEY_LEFT:
1084 	case KEY_RIGHT:
1085 	case ' ':
1086 	case 'h':
1087 	case 'l':
1088 	case 'H':
1089 	case 'L':	if (ch == 'd')
1090 			    (void) sprintf(line,"deletecol [range] ");
1091 			else if (ch == 'y')
1092 			    (void) sprintf(line,"yankcol [range] ");
1093 			else if (ch == 'Z')
1094 			    (void) sprintf(line,"hide [range] ");
1095 			else
1096 			    return (0);
1097 			edit_mode();
1098 			write_line('A');
1099 			startshow();
1100 			showrange = SHOWCOLS;
1101 			showsc = curcol;
1102 			ungetch(c);
1103 			return (ESC);
1104 
1105 	default:	return (0);
1106     }
1107     /*NOTREACHED*/
1108 }
1109 
1110 /* delete numrow rows, starting with rs */
1111 void
closerow(int rs,int numrow)1112 closerow(int rs, int numrow)
1113 {
1114     register struct ent **pp;
1115     int			r, c, i;
1116     struct ent		**tmprow;
1117 
1118     if (rs + numrow - 1 > maxrow) return;
1119     r = rs;
1120 
1121 /*
1122  * Rows are dealt with in numrow groups, each group of rows spaced numrow
1123  * rows apart.
1124  */
1125     for (i = 0; i < numrow; i++) {
1126 	r = rs + i;
1127 
1128 	/* save the first row of the group and empty it out */
1129 	tmprow = tbl[r];
1130 	pp = ATBL(tbl, r, 0);
1131 	for (c = maxcol + 1; --c >= 0; pp++) {
1132 	    if (*pp) {
1133 		free_ent(*pp, 1);
1134 		*pp = (struct ent *)0;
1135 	    }
1136 	}
1137 
1138 	/* move the rows, put the deleted, but now empty, row at the end */
1139 	for (; r + numrow < maxrows - 1; r += numrow) {
1140 	    row_hidden[r] = row_hidden[r + numrow];
1141 	    tbl[r] = tbl[r + numrow];
1142 	    pp = ATBL(tbl, r, 0);
1143 	    for (c = 0; c < maxcols; c++, pp++)
1144 		if (*pp)
1145 		    (*pp)->row = r;
1146 	}
1147 	tbl[r] = tmprow;
1148     }
1149 
1150     /* Update all marked cells. */
1151     for (i = 0; i < 37; i++) {
1152 	if (savedrow[i] >= rs && savedrow[i] < rs + numrow)
1153 	    savedrow[i] = savedcol[i] = -1;
1154 	if (savedrow[i] >= rs + numrow)
1155 	    savedrow[i] -= numrow;
1156 	if (savedstrow[i] >= rs && savedstrow[i] < rs + numrow)
1157 	    savedstrow[i] = rs;
1158 	if (savedstrow[i] >= rs + numrow)
1159 	    savedstrow[i] -= numrow;
1160     }
1161     if (gs.g_row >= rs && gs.g_row < rs + numrow)
1162 	gs.g_row = rs;
1163     if (gs.g_row >= rs + numrow)
1164 	gs.g_row -= numrow;
1165     if (gs.g_lastrow >= rs && gs.g_lastrow < rs + numrow)
1166 	gs.g_lastrow = rs - 1;
1167     if (gs.g_lastrow >= rs + numrow)
1168 	gs.g_lastrow -= numrow;
1169     if (gs.g_row > gs.g_lastrow)
1170 	gs.g_row = gs.g_col = -1;
1171     if (gs.strow >= rs && gs.strow < rs + numrow)
1172 	gs.strow = rs;
1173     if (gs.strow >= rs + numrow)
1174 	gs.strow -= numrow;
1175 
1176     maxrow -= numrow;
1177 
1178     /* Update note links. */
1179     for (r = 0; r <= maxrow; r++) {
1180 	for (c = 0; c <= maxcol; c++) {
1181 	    pp = ATBL(tbl, r, c);
1182 	    if (*pp) {
1183 		if ((*pp)->nrow >= rs && (*pp)->nrow < rs + numrow)
1184 		    (*pp)->nrow = rs;
1185 		if ((*pp)->nrow >= rs + numrow)
1186 		    ((*pp)->nrow) -= numrow;
1187 		if ((*pp)->nlastrow >= rs && (*pp)->nlastrow < rs + numrow)
1188 		    (*pp)->nlastrow = rs - 1;
1189 		if ((*pp)->nlastrow >= rs + numrow)
1190 		    ((*pp)->nlastrow) -= numrow;
1191 		if ((*pp)->nlastrow < (*pp)->nrow)
1192 		    (*pp)->nrow = (*pp)->ncol = -1;
1193 	    }
1194 	}
1195     }
1196     FullUpdate++;
1197     modflg++;
1198 }
1199 
1200 /* delete group of columns (1 or more) */
1201 void
closecol(int arg)1202 closecol(int arg)
1203 {
1204     int r, c, i;
1205     int cs = maxcol - curcol + 1;
1206     struct ent **pp;
1207     struct ent *p;
1208     struct ent *obuf = NULL;
1209     char buf[50];
1210 
1211     if (cs - arg < 0) {
1212     	cs = cs > 0 ? cs : 0;
1213 	(void) sprintf(buf, "Can't delete %d column%s %d column%s left", arg,
1214 		(arg != 1 ? "s," : ","), cs, (cs != 1 ? "s" : ""));
1215 	error(buf);
1216 	return;
1217     }
1218     if (any_locked_cells(0, curcol, maxrow, curcol + arg - 1)) {
1219 	error("Locked cells encountered. Nothing changed");
1220 	return;
1221     }
1222     if (dbidx < 0) dbidx++;
1223     delbuf[dbidx] = delbuf[DELBUFSIZE - 1];
1224     delbuf[DELBUFSIZE - 1] = NULL;
1225     delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 1];
1226     delbuffmt[DELBUFSIZE - 1] = NULL;
1227     for (i = dbidx + 1; i < DELBUFSIZE; i++) {
1228 	if (delbuf[i] == delbuf[dbidx]) {
1229 	    delbuf[dbidx] = NULL;
1230 	    delbuffmt[dbidx] = NULL;
1231 	    break;
1232 	}
1233     }
1234     flush_saved();
1235     if (qbuf) {
1236 	if (dbidx < 0) dbidx++;
1237 	delbuf[dbidx] = delbuf[qbuf];
1238 	delbuffmt[dbidx] = delbuffmt[qbuf];
1239 	flush_saved();
1240 	obuf = delbuf[qbuf];	/* orig. contents of the del. buffer */
1241     }
1242     sync_refs();
1243     erase_area(0, curcol, maxrow, curcol + arg - 1, 0);
1244     fix_ranges(-1, curcol, -1, curcol + arg - 1, -1, -1);
1245     for (i = 0; i < DELBUFSIZE; i++)
1246 	if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) {
1247 	    delbuf[i] = delbuf[dbidx];
1248 	    delbuffmt[i] = delbuffmt[dbidx];
1249 	}
1250     qbuf = 0;
1251     for (i = DELBUFSIZE - 1; i > DELBUFSIZE - 9; i--) {
1252 	delbuf[i] = delbuf[i-1];
1253 	delbuffmt[i] = delbuffmt[i-1];
1254     }
1255     delbuf[DELBUFSIZE - 9] = delbuf[dbidx];
1256     delbuffmt[DELBUFSIZE - 9] = delbuffmt[dbidx];
1257     for (p = delbuf[dbidx]; p; p = p->next)
1258 	p->flags &= ~may_sync;
1259 
1260     /* clear then copy the block left */
1261     cs = maxcols - arg - 1;
1262     for (r=0; r<=maxrow; r++) {
1263 	for (c = curcol; c - curcol < arg; c++)
1264 	    if ((p = *ATBL(tbl, r, c)))
1265 		free_ent(p, 1);
1266 
1267 	pp = ATBL(tbl, r, curcol);
1268 	for (c=curcol; c <= cs; c++, pp++) {
1269 	    if (c > cs)
1270 		*pp = (struct ent *)0;
1271 	    else if ((pp[0] = pp[arg]))
1272 		pp[0]->col -= arg;
1273 	}
1274 
1275 	c = arg;
1276 	for (; --c >= 0; pp++)
1277 	    *pp = (struct ent *)0;
1278     }
1279 
1280     for (i = curcol; i < maxcols - arg - 1; i++) {
1281 	fwidth[i] = fwidth[i+arg];
1282 	precision[i] = precision[i+arg];
1283 	realfmt[i] = realfmt[i+arg];
1284 	col_hidden[i] = col_hidden[i+arg];
1285     }
1286     for (; i < maxcols - 1; i++) {
1287 	fwidth[i] = DEFWIDTH;
1288 	precision[i] = DEFPREC;
1289 	realfmt[i] = DEFREFMT;
1290 	col_hidden[i] = FALSE;
1291     }
1292 
1293     /* Update all marked cells. */
1294     for (i = 0; i < 37; i++) {
1295 	if (savedcol[i] >= curcol && savedcol[i] < curcol + arg)
1296 	    savedrow[i] = savedcol[i] = -1;
1297 	if (savedcol[i] >= curcol + arg)
1298 	    savedcol[i] -= arg;
1299 	if (savedstcol[i] >= curcol && savedstcol[i] < curcol + arg)
1300 	    savedstcol[i] = curcol;
1301 	if (savedstcol[i] >= curcol + arg)
1302 	    savedstcol[i] -= arg;
1303     }
1304     if (gs.g_col >= curcol && gs.g_col < curcol + arg)
1305 	gs.g_col = curcol;
1306     if (gs.g_col >= curcol + arg)
1307 	gs.g_col -= arg;
1308     if (gs.g_lastcol >= curcol && gs.g_lastcol < curcol + arg)
1309 	gs.g_lastcol = curcol - 1;
1310     if (gs.g_lastcol >= curcol + arg)
1311 	gs.g_lastcol -= arg;
1312     if (gs.g_col > gs.g_lastcol)
1313 	gs.g_row = gs.g_col = -1;
1314     if (gs.stcol >= curcol && gs.stcol < curcol + arg)
1315 	gs.stcol = curcol;
1316     if (gs.stcol >= curcol + arg)
1317 	gs.stcol -= arg;
1318 
1319     maxcol -= arg;
1320 
1321     /* Update note links. */
1322     for (r = 0; r <= maxrow; r++) {
1323 	for (c = 0; c <= maxcol; c++) {
1324 	    pp = ATBL(tbl, r, c);
1325 	    if (*pp) {
1326 		if ((*pp)->ncol >= curcol && (*pp)->ncol < curcol + arg)
1327 		    (*pp)->ncol = curcol;
1328 		if ((*pp)->ncol >= curcol + arg)
1329 		    ((*pp)->ncol) -= arg;
1330 		if ((*pp)->nlastcol >= curcol && (*pp)->nlastcol < curcol + arg)
1331 		    (*pp)->nlastcol = curcol - 1;
1332 		if ((*pp)->nlastcol >= curcol + arg)
1333 		    ((*pp)->nlastcol) -= arg;
1334 		if ((*pp)->nlastcol < (*pp)->ncol)
1335 		    (*pp)->nrow = (*pp)->ncol = -1;
1336 	    }
1337 	}
1338     }
1339     rowsinrange = 1;
1340     colsinrange = fwidth[curcol];
1341 
1342     FullUpdate++;
1343     modflg++;
1344 }
1345 
1346 void
doend(int rowinc,int colinc)1347 doend(int rowinc, int colinc)
1348 {
1349     register struct ent *p;
1350     int r, c;
1351 
1352     if (!loading)
1353 	remember(0);
1354 
1355     if (VALID_CELL(p, currow, curcol)) {
1356 	r = currow + rowinc;
1357 	c = curcol + colinc;
1358 	if (r >= 0 && r < maxrows &&
1359 	    c >= 0 && c < maxcols &&
1360 	    !VALID_CELL(p, r, c)) {
1361 		currow = r;
1362 		curcol = c;
1363 	}
1364     }
1365 
1366     if (!VALID_CELL(p, currow, curcol)) {
1367         switch (rowinc) {
1368         case -1:
1369 	    while (!VALID_CELL(p, currow, curcol) && currow > 0)
1370 		currow--;
1371 	    break;
1372         case  1:
1373 	    while (!VALID_CELL(p, currow, curcol) && currow < maxrows-1)
1374 		currow++;
1375 	    break;
1376         case  0:
1377             switch (colinc) {
1378  	    case -1:
1379 	        while (!VALID_CELL(p, currow, curcol) && curcol > 0)
1380 		    curcol--;
1381 	        break;
1382  	    case  1:
1383 	        while (!VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
1384 		    curcol++;
1385 	        break;
1386 	    }
1387             break;
1388         }
1389 	rowsinrange = 1;
1390 	colsinrange = fwidth[curcol];
1391 	if (!loading)
1392 	    remember(1);
1393 
1394 	error ("");	/* clear line */
1395 	return;
1396     }
1397 
1398     switch (rowinc) {
1399     case -1:
1400 	while (VALID_CELL(p, currow, curcol) && currow > 0)
1401 	    currow--;
1402 	break;
1403     case  1:
1404 	while (VALID_CELL(p, currow, curcol) && currow < maxrows-1)
1405 	    currow++;
1406 	break;
1407     case  0:
1408 	switch (colinc) {
1409 	case -1:
1410 	    while (VALID_CELL(p, currow, curcol) && curcol > 0)
1411 		curcol--;
1412 	    break;
1413 	case  1:
1414 	    while (VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
1415 		curcol++;
1416 	    break;
1417 	}
1418 	break;
1419     }
1420     if (!VALID_CELL(p, currow, curcol)) {
1421         currow -= rowinc;
1422         curcol -= colinc;
1423     }
1424     rowsinrange = 1;
1425     colsinrange = fwidth[curcol];
1426 }
1427 
1428 /* Modified 9/17/90 THA to handle more formats */
1429 void
doformat(int c1,int c2,int w,int p,int r)1430 doformat(int c1, int c2, int w, int p, int r)
1431 {
1432     register int i;
1433     int crows = 0;
1434     int ccols = c2;
1435 
1436     if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ;
1437     if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ;
1438 
1439     if (w == 0) {
1440 	error("Width too small - setting to 1");
1441 	w = 1;
1442     }
1443 
1444     if (usecurses && w > COLS - rescol - 2) {
1445 	error("Width too large - Maximum = %d", COLS - rescol - 2);
1446 	w = COLS - rescol - 2;
1447     }
1448 
1449     if (p > w) {
1450 	error("Precision too large");
1451 	p = w;
1452     }
1453 
1454     checkbounds(&crows, &ccols);
1455     if (ccols < c2) {
1456 	error("Format statement failed to create implied column %d", c2);
1457 	return;
1458     }
1459 
1460     for (i = c1; i <= c2; i++)
1461 	fwidth[i] = w, precision[i] = p, realfmt[i] = r;
1462 
1463     rowsinrange = 1;
1464     colsinrange = fwidth[curcol];
1465 
1466     FullUpdate++;
1467     modflg++;
1468 }
1469 
1470 void
formatcol(arg)1471 formatcol(arg)
1472 {
1473     int c, i;
1474     int mf = modflg;
1475     int *oldformat;
1476 
1477     error("Current format is %d %d %d",
1478 	    fwidth[curcol], precision[curcol],
1479 	    realfmt[curcol]);
1480     refresh();
1481     oldformat = (int *)scxmalloc(arg*3*sizeof(int));
1482     for (i = 0; i < arg; i++) {
1483 	oldformat[i * 3 + 0] = fwidth[i + curcol];
1484 	oldformat[i * 3 + 1] = precision[i + curcol];
1485 	oldformat[i * 3 + 2] = realfmt[i + curcol];
1486     }
1487     c = nmgetch();
1488     while (c >= 0 && c != ctl('m') && c != 'q' && c != ESC &&
1489 	    c != ctl('g') && linelim < 0) {
1490 	if (c >= '0' && c <= '9')
1491 	    for (i = curcol; i < curcol + arg; i++)
1492 		realfmt[i] = c - '0';
1493 	else
1494 	    switch (c) {
1495 		case KEY_LEFT:
1496 		case '<':
1497 		case 'h':
1498 		    for (i = curcol; i < curcol + arg; i++) {
1499 			fwidth[i]--;
1500 			if (fwidth[i] < 1)
1501 			    fwidth[i] = 1;
1502 		    }
1503 		    rowsinrange = 1;
1504 		    colsinrange = fwidth[curcol];
1505 		    modflg++;
1506 		    break;
1507 		case KEY_RIGHT:
1508 		case '>':
1509 		case 'l':
1510 		    for (i = curcol; i < curcol + arg; i++) {
1511 			fwidth[i]++;
1512 			if (fwidth[i] > COLS - rescol - 2)
1513 			    fwidth[i] = COLS - rescol - 2;
1514 		    }
1515 		    rowsinrange = 1;
1516 		    colsinrange = fwidth[curcol];
1517 		    modflg++;
1518 		    break;
1519 		case KEY_DOWN:
1520 		case '-':
1521 		case 'j':
1522 		    for (i = curcol; i < curcol + arg; i++) {
1523 			precision[i]--;
1524 			if (precision[i] < 0)
1525 			    precision[i] = 0;
1526 		    }
1527 		    modflg++;
1528 		    break;
1529 		case KEY_UP:
1530 		case '+':
1531 		case 'k':
1532 		    for (i = curcol; i < curcol + arg; i++)
1533 			precision[i]++;
1534 		    modflg++;
1535 		    break;
1536 		case ' ':
1537 		    if (arg == 1)
1538 			(void) sprintf(line,
1539 				"format [for column] %s ",
1540 				coltoa(curcol));
1541 		    else {
1542 			(void) sprintf(line,
1543 				"format [for columns] %s:",
1544 				coltoa(curcol));
1545 			(void) sprintf(line+strlen(line), "%s ",
1546 				coltoa(curcol+arg-1));
1547 		    }
1548 		    linelim = strlen(line);
1549 		    insert_mode();
1550 		    error("Current format is %d %d %d",
1551 			    fwidth[curcol], precision[curcol],
1552 			    realfmt[curcol]);
1553 		    continue;
1554 		case '=':
1555 		    error("Define format type (0-9):");
1556 		    refresh();
1557 		    if ((c = nmgetch()) >= '0' && c <= '9') {
1558 			if (colformat[c-'0']) {
1559 			    (void) sprintf(line,
1560 				    "format %c = \"%s\"", c, colformat[c-'0']);
1561 			    edit_mode();
1562 			    linelim = strlen(line) - 1;
1563 			} else {
1564 			    (void) sprintf(line,
1565 				    "format %c = \"", c);
1566 			    insert_mode();
1567 			    linelim = strlen(line);
1568 			}
1569 			error("");
1570 		    } else {
1571 			error("Invalid format type");
1572 			c = -1;
1573 		    }
1574 		    continue;
1575 		case ctl('l'):
1576 		    FullUpdate++;
1577 		    clearok(stdscr, 1);
1578 		    break;
1579 		default:
1580 		    break;
1581 	    }
1582 	error("Current format is %d %d %d",
1583 		fwidth[curcol], precision[curcol],
1584 		realfmt[curcol]);
1585 	FullUpdate++;
1586 	update(1);
1587 	refresh();
1588 	if (linelim < 0)
1589 	    if ((c = nmgetch()) == ESC || c == ctl('g') || c == 'q') {
1590 		for (i = 0; i < arg; i++) {
1591 		    fwidth[i + curcol] = oldformat[i * 3 + 0];
1592 		    precision[i + curcol] = oldformat[i * 3 + 1];
1593 		    realfmt[i + curcol] = oldformat[i * 3 + 2];
1594 		}
1595 		modflg = mf;
1596 		FullUpdate++;
1597 		update(1);
1598 	    }
1599     }
1600     scxfree((char *)oldformat);
1601     if (c >= 0)
1602 	error("");
1603 }
1604 
1605 void
ljustify(sr,sc,er,ec)1606 ljustify(sr, sc, er, ec)
1607 {
1608     struct ent *p;
1609     int i, j;
1610 
1611     if (sr > er) {
1612 	i = sr;
1613 	sr = er;
1614 	er = i;
1615     }
1616     if (sc > ec) {
1617 	i = sc;
1618 	sc = ec;
1619 	ec = i;
1620     }
1621     for (i = sr; i <= er; i++) {
1622 	for (j = sc; j <= ec; j++) {
1623 	    p = *ATBL(tbl, i, j);
1624 	    if (p && p->label) {
1625 		p->flags &= ~is_label;
1626 		p->flags |= is_leftflush | is_changed;
1627 		changed++;
1628 		modflg++;
1629 	    }
1630 	}
1631     }
1632 }
1633 
1634 void
rjustify(sr,sc,er,ec)1635 rjustify(sr, sc, er, ec)
1636 {
1637     struct ent *p;
1638     int i, j;
1639 
1640     if (sr > er) {
1641 	i = sr;
1642 	sr = er;
1643 	er = i;
1644     }
1645     if (sc > ec) {
1646 	i = sc;
1647 	sc = ec;
1648 	ec = i;
1649     }
1650     for (i = sr; i <= er; i++) {
1651 	for (j = sc; j <= ec; j++) {
1652 	    p = *ATBL(tbl, i, j);
1653 	    if (p && p->label) {
1654 		p->flags &= ~(is_label | is_leftflush);
1655 		p->flags |= is_changed;
1656 		changed++;
1657 		modflg++;
1658 	    }
1659 	}
1660     }
1661 }
1662 
1663 void
center(sr,sc,er,ec)1664 center(sr, sc, er, ec)
1665 {
1666     struct ent *p;
1667     int i, j;
1668 
1669     if (sr > er) {
1670 	i = sr;
1671 	sr = er;
1672 	er = i;
1673     }
1674     if (sc > ec) {
1675 	i = sc;
1676 	sc = ec;
1677 	ec = i;
1678     }
1679     for (i = sr; i <= er; i++) {
1680 	for (j = sc; j <= ec; j++) {
1681 	    p = *ATBL(tbl, i, j);
1682 	    if (p && p->label) {
1683 		p->flags &= ~is_leftflush;
1684 		p->flags |= is_label | is_changed;
1685 		changed++;
1686 		modflg++;
1687 	    }
1688 	}
1689     }
1690 }
1691 
1692 void
print_options(FILE * f)1693 print_options(FILE *f)
1694 {
1695     if (
1696 	autocalc &&
1697 	!autoinsert &&
1698 	!autowrap &&
1699 	!cslop &&
1700 	!optimize &&
1701 	!rndtoeven &&
1702 	propagation == 10 &&
1703 	calc_order == BYROWS &&
1704 	!numeric &&
1705 	prescale == 1.0 &&
1706 	!extfunc &&
1707 	showtop &&
1708 	tbl_style == 0 &&
1709 	craction == 0 &&
1710 	pagesize == 0 &&
1711 	rowlimit == -1 &&
1712 	collimit == -1 &&
1713 	!color &&
1714 	!colorneg &&
1715 	!colorerr
1716        )
1717 	return;		/* No reason to do this */
1718 
1719     (void) fprintf(f, "set");
1720     if (!autocalc)
1721 	(void) fprintf(f," !autocalc");
1722     if (autoinsert)
1723 	(void) fprintf(f," autoinsert");
1724     if (autowrap)
1725 	(void) fprintf(f," autowrap");
1726     if (cslop)
1727 	(void) fprintf(f," cslop");
1728     if (optimize)
1729 	(void) fprintf(f," optimize");
1730     if (rndtoeven)
1731 	(void) fprintf(f, " rndtoeven");
1732     if (propagation != 10)
1733 	(void) fprintf(f, " iterations = %d", propagation);
1734     if (calc_order != BYROWS )
1735 	(void) fprintf(f, " bycols");
1736     if (numeric)
1737 	(void) fprintf(f, " numeric");
1738     if (prescale != 1.0)
1739 	(void) fprintf(f, " prescale");
1740     if (extfunc)
1741 	(void) fprintf(f, " extfun");
1742     if (!showtop)
1743 	(void) fprintf(f, " !toprow");
1744     if (tbl_style)
1745 	(void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" :
1746 					tbl_style == LATEX ? "latex" :
1747 					tbl_style == SLATEX ? "slatex" :
1748 					tbl_style == TEX ? "tex" :
1749 					tbl_style == FRAME ? "frame" : "0" );
1750     if (craction)
1751 	(void) fprintf(f, " craction = %d", craction);
1752     if (pagesize)
1753 	(void) fprintf(f, " pagesize = %d", pagesize);
1754     if (rowlimit >= 0)
1755 	(void) fprintf(f, " rowlimit = %d", rowlimit);
1756     if (collimit >= 0)
1757 	(void) fprintf(f, " collimit = %d", collimit);
1758     if (color)
1759 	(void) fprintf(f," color");
1760     if (colorneg)
1761 	(void) fprintf(f," colorneg");
1762     if (colorerr)
1763 	(void) fprintf(f," colorerr");
1764     (void) fprintf(f, "\n");
1765 }
1766 
1767 void
printfile(char * fname,int r0,int c0,int rn,int cn)1768 printfile(char *fname, int r0, int c0, int rn, int cn)
1769 {
1770     FILE *f;
1771     static char *pline = NULL;		/* only malloc once, malloc is slow */
1772     static unsigned fbufs_allocated = 0;
1773     int plinelim;
1774     int pid;
1775     int fieldlen, nextcol;
1776     long namelen;
1777     int row, col;
1778     register struct ent **pp;
1779     char file[256];
1780     char path[1024];
1781     char *tpp;
1782 
1783     if (fname) {
1784 	/* printfile will be the [path/]file ---> [path/]file.out */
1785 	if (*fname == '\0') {
1786 	    strcpy(path, curfile);
1787 
1788 #ifdef MSDOS
1789 	    namelen = 12;
1790 	    if ((tpp = strrchr(path, '\\')) == NULL) {
1791 #else
1792 	    if ((tpp = strrchr(path, '/')) == NULL) {
1793 		namelen = pathconf(".", _PC_NAME_MAX);
1794 #endif
1795 		tpp = path;
1796 	    } else {
1797 #ifndef MSDOS
1798 		*tpp = '\0';
1799 		namelen = pathconf(path, _PC_NAME_MAX);
1800 		*tpp = '/';
1801 #endif
1802 		tpp++;
1803 	    }
1804 	    strcpy(file, tpp);
1805 
1806 	    if (!strcmp(file + strlen(file) - 3, ".sc"))
1807 		file[strlen(file) - 3] = '\0';
1808 	    else if (scext != NULL && file[strlen(file) - strlen(scext) - 1] == '.'
1809 		    && !strcmp(file + strlen(file) - strlen(scext), scext))
1810 		file[strlen(file) - strlen(scext)] = '\0';
1811 
1812 	    if (ascext == NULL)
1813 		file[namelen - 4] = '\0';
1814 	    else
1815 		file[namelen - strlen(ascext) - 1] = '\0';
1816 	    sprintf(tpp, "%s.%s", file, ascext == NULL ? "asc" : ascext);
1817 	    fname = path;
1818 	}
1819 
1820 	if ((strcmp(fname, curfile) == 0) &&
1821 	    !yn_ask("Confirm that you want to destroy the data base: (y,n)")) {
1822 	    return;
1823 	}
1824 
1825 	if ((f = openfile(fname, &pid, NULL)) == (FILE *)0) {
1826 	    error("Can't create file \"%s\"", fname);
1827 	    return;
1828 	}
1829     } else
1830 	f = stdout;
1831 
1832     if (!pline && (pline = scxmalloc((unsigned)(FBUFLEN *
1833 	    ++fbufs_allocated))) == (char *)NULL) {
1834 	error("Malloc failed in printfile()");
1835 	return;
1836     }
1837 
1838     for (row = r0; row <= rn; row++) {
1839 	int c = 0;
1840 
1841 	if (row_hidden[row])
1842 	    continue;
1843 
1844 	pline[plinelim=0] = '\0';
1845 	for (pp = ATBL(tbl, row, col=c0); col<=cn;
1846 	        pp += nextcol-col, col = nextcol, c += fieldlen) {
1847 
1848 	    nextcol = col+1;
1849 	    if (col_hidden[col]) {
1850 		fieldlen = 0;
1851 		continue;
1852 	    }
1853 
1854 	    fieldlen = fwidth[col];
1855 	    if (*pp) {
1856 		char *s;
1857 
1858 		/*
1859 		 * dynamically allocate pline, making sure we are not
1860 		 * attempting to write 'out of bounds'.
1861 		 */
1862 		while (c > (fbufs_allocated * FBUFLEN)) {
1863 		    if ((pline = scxrealloc ((char *)pline,
1864 			    (unsigned)(FBUFLEN * ++fbufs_allocated))) == NULL) {
1865 			error ("Realloc failed in printfile()");
1866 			return;
1867 		    }
1868 		}
1869 		while (plinelim<c) pline[plinelim++] = ' ';
1870 		plinelim = c;
1871 		if ((*pp)->flags&is_valid) {
1872 		    while(plinelim + fwidth[col] >
1873 			  (fbufs_allocated * FBUFLEN)) {
1874 		      if((pline = ((char *)scxrealloc
1875 				   ((char *)pline,
1876 				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
1877 			 == NULL) {
1878 			error("Realloc failed in printfile()");
1879 			return;
1880 		      }
1881 		    }
1882 		    if ((*pp)->cellerror)
1883 			(void) sprintf(pline+plinelim, "%*s",
1884 				fwidth[col], ((*pp)->cellerror == CELLERROR ?
1885 				"ERROR " : "INVALID "));
1886 		    else {
1887 		      char *cfmt;
1888 
1889 		      cfmt = (*pp)->format ? (*pp)->format :
1890 			    (realfmt[col] >= 0 && realfmt[col] < COLFORMATS &&
1891 			    colformat[realfmt[col]]) ?
1892 			    colformat[realfmt[col]] : 0;
1893 		      if (cfmt) {
1894 	   	        char field[FBUFLEN];
1895 
1896 			if (*cfmt == ctl('d')) {
1897 			    time_t v = (time_t) ((*pp)->v);
1898 			    strftime(field, sizeof(field), cfmt + 1,
1899 				    localtime(&v));
1900 			    sprintf(pline+plinelim, "%-*s", fwidth[col],
1901 				    field);
1902 			} else {
1903 			    format(cfmt, precision[col], (*pp)->v, field,
1904 				    sizeof(field));
1905 			    (void) sprintf(pline+plinelim, "%*s", fwidth[col],
1906 				    field);
1907 			}
1908 		      } else {
1909 	   	        char field[FBUFLEN];
1910 			(void) engformat(realfmt[col], fwidth[col],
1911                                              precision[col], (*pp) -> v,
1912                                              field, sizeof(field));
1913 			(void) sprintf(pline+plinelim, "%*s", fwidth[col],
1914 				       field);
1915 		      }
1916 		    }
1917 		    plinelim += strlen(pline+plinelim);
1918 		}
1919 		if ((s = (*pp)->label)) {
1920 		    int slen;
1921 		    char *start, *last;
1922 		    register char *fp;
1923 		    struct ent *nc;
1924 
1925 		    /*
1926 		     * Figure out if the label slops over to a blank field.
1927 		     * A string started with backslash is defining repetition
1928 		     * char
1929 		     */
1930 		    slen = strlen(s);
1931 		    if (*s == '\\' && *(s+1) != '\0')
1932 			slen = fwidth[col];
1933 		    while (slen > fieldlen && nextcol <= cn &&
1934 			    !((nc = lookat(row,nextcol))->flags & is_valid) &&
1935 			    !(nc->label)) {
1936 
1937 	                if (!col_hidden[nextcol])
1938 		 	    fieldlen += fwidth[nextcol];
1939 
1940 			nextcol++;
1941 		    }
1942 		    if (slen > fieldlen)
1943 			slen = fieldlen;
1944 
1945 		    while(c + fieldlen + 2 > (fbufs_allocated * FBUFLEN)) {
1946 		      if((pline = ((char *)scxrealloc
1947 				   ((char *)pline,
1948 				    (unsigned)(FBUFLEN * ++fbufs_allocated))))
1949 			 == NULL) {
1950 			error ("scxrealloc failed in printfile()");
1951 			return;
1952 		      }
1953 		    }
1954 
1955 		    /* Now justify and print */
1956 		    start = (*pp)->flags & is_leftflush ? pline + c
1957 					: pline + c + fieldlen - slen;
1958 		    if( (*pp)->flags & is_label )
1959 			start = pline + (c + ((fwidth[col]>slen)?(fwidth[col]-slen)/2:0));
1960 		    last = pline + c + fieldlen;
1961 		    fp = plinelim < c ? pline + plinelim : pline + c;
1962 		    while (fp < start)
1963 			*fp++ = ' ';
1964 		    if( *s == '\\' && *(s+1)!= '\0' ) {
1965 			char *strt;
1966 			strt = ++s;
1967 
1968 			while(slen--) {
1969 				*fp++ = *s++; if( *s == '\0' ) s = strt;
1970 			}
1971 		    }
1972 		    else
1973 		    while (slen--)
1974 			*fp++ = *s++;
1975 
1976 		    if (!((*pp)->flags & is_valid) || fieldlen != fwidth[col])
1977 			while(fp < last)
1978 			    *fp++ = ' ';
1979 		    if (plinelim < fp - pline)
1980 			plinelim = fp - pline;
1981 		}
1982 	    }
1983 	}
1984 	pline[plinelim++] = '\n';
1985 	pline[plinelim] = '\0';
1986 	(void) fputs (pline, f);
1987     }
1988 
1989     if (fname) closefile(f, pid, 0);
1990 }
1991 
1992 void
1993 tblprintfile(char *fname, int r0, int c0, int rn, int cn)
1994 {
1995     FILE *f;
1996     int pid;
1997     long namelen;
1998     int row, col;
1999     register struct ent **pp;
2000     char coldelim = DEFCOLDELIM;
2001     char file[256];
2002     char path[1024];
2003     char *tpp;
2004 
2005     /* tblprintfile will be the [path/]file ---> [path/]file.out */
2006     if (*fname == '\0') {
2007 	strcpy(path, curfile);
2008 
2009 #ifdef MSDOS
2010 	namelen = 12;
2011 	if ((tpp = strrchr(path, '\\')) == NULL) {
2012 #else
2013 	if ((tpp = strrchr(path, '/')) == NULL) {
2014 	    namelen = pathconf(".", _PC_NAME_MAX);
2015 #endif
2016 	    tpp = path;
2017 	} else {
2018 #ifndef MSDOS
2019 	    *tpp = '\0';
2020 	    namelen = pathconf(path, _PC_NAME_MAX);
2021 	    *tpp = '/';
2022 #endif
2023 	    tpp++;
2024 	}
2025 	strcpy(file, tpp);
2026 
2027 	if (!strcmp(file + strlen(file) - 3, ".sc"))
2028 	    file[strlen(file) - 3] = '\0';
2029 	else if (scext != NULL && file[strlen(file) - strlen(scext) - 1] == '.'
2030 		&& !strcmp(file + strlen(file) - strlen(scext), scext))
2031 	    file[strlen(file) - strlen(scext)] = '\0';
2032 
2033 	if (tbl_style == 0) {
2034 	    if (tbl0ext == NULL)
2035 		file[namelen - 4] = '\0';
2036 	    else
2037 		file[namelen - strlen(tbl0ext) - 1] = '\0';
2038 	    sprintf(tpp, "%s.%s", file, tbl0ext == NULL ? "cln" : tbl0ext);
2039 	}
2040 	else if (tbl_style == TBL) {
2041 	    if (tblext == NULL)
2042 		file[namelen - 4] = '\0';
2043 	    else
2044 		file[namelen - strlen(tblext) - 1] = '\0';
2045 	    sprintf(tpp, "%s.%s", file, tblext == NULL ? "tbl" : tblext);
2046 	}
2047 	else if (tbl_style == LATEX) {
2048 	    if (latexext == NULL)
2049 		file[namelen - 4] = '\0';
2050 	    else
2051 		file[namelen - strlen(latexext) - 1] = '\0';
2052 	    sprintf(tpp, "%s.%s", file, latexext == NULL ? "lat" : latexext);
2053 	}
2054 	else if (tbl_style == SLATEX) {
2055 	    if (slatexext == NULL)
2056 		file[namelen - 4] = '\0';
2057 	    else
2058 		file[namelen - strlen(slatexext) - 1] = '\0';
2059 	    sprintf(tpp, "%s.%s", file, slatexext == NULL ? "stx" : slatexext);
2060 	}
2061 	else if (tbl_style == TEX) {
2062 	    if (texext == NULL)
2063 		file[namelen - 4] = '\0';
2064 	    else
2065 		file[namelen - strlen(texext) - 1] = '\0';
2066 	    sprintf(tpp, "%s.%s", file, texext == NULL ? "tex" : texext);
2067 	}
2068 	fname = path;
2069     }
2070 
2071     if ((strcmp(fname, curfile) == 0) &&
2072 	!yn_ask("Confirm that you want to destroy the data base: (y,n)"))
2073 	    return;
2074 
2075     if ((f = openfile(fname, &pid, NULL)) == (FILE *)0) {
2076 	error ("Can't create file \"%s\"", fname);
2077 	return;
2078     }
2079 
2080     if ( tbl_style == TBL ) {
2081 	fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname);
2082 	fprintf(f,"tab(%c);\n",coldelim);
2083 	for (col=c0;col<=cn; col++) fprintf(f," n");
2084 	fprintf(f, ".\n");
2085     }
2086     else if ( tbl_style == LATEX ) {
2087 	fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname);
2088 	for (col=c0;col<=cn; col++) fprintf(f,"c");
2089 	fprintf(f, "}\n");
2090 	coldelim = '&';
2091     }
2092     else if ( tbl_style == SLATEX ) {
2093 	fprintf(f,"%% ** %s spreadsheet output\n!begin<tabular><",progname);
2094 	for (col=c0;col<=cn; col++) fprintf(f,"c");
2095 	fprintf(f, ">\n");
2096 	coldelim = '&';
2097     }
2098     else if ( tbl_style == TEX ) {
2099 	fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n",
2100 		progname, cn-c0+1);
2101 	coldelim = '&';
2102     }
2103     else if ( tbl_style == FRAME ) {
2104 	fprintf(f,"<MIFFile 3.00> # generated by the sc spreadsheet calculator\n");
2105 	fprintf(f,"<Tbls\n");
2106 	fprintf(f," <Tbl \n");
2107 	fprintf(f,"  <TblID 1> # This table's ID is 1\n");
2108 	fprintf(f,"  <TblFormat \n");
2109 	fprintf(f,"   <TblTag `Format A'> # Table Format Catalog\n");
2110 	fprintf(f,"  > # end of TblFormat\n");
2111 	fprintf(f,"  <TblNumColumns %d> # Has %d columns\n",cn-c0+1,cn-c0+1);
2112 	fprintf(f,"  <TblTitleContent\n");
2113 	fprintf(f,"   <Para\n");
2114 	fprintf(f,"    <PgfTag `TableTitle'> # Forces lookup in Paragraph Format Catalog\n");
2115 	fprintf(f,"    <ParaLine\n");
2116 	fprintf(f,"     <String `%s'>\n",fname);
2117 	fprintf(f,"    > # end of ParaLine\n");
2118 	fprintf(f,"   > # end of Para\n");
2119 	fprintf(f,"  > # end of TblTitleContent\n");
2120 	fprintf(f,"  <TblH # The heading\n");
2121 	fprintf(f,"   <Row # The heading row\n");
2122 	for (col=c0; col <= cn; col++) {
2123 	    fprintf(f,"    <Cell <CellContent <Para # Cell in column \n");
2124 	    fprintf(f,"       <PgfTag `CellHeading'> # in Paragraph Format Catalog\n");
2125 	    fprintf(f,"       <ParaLine <String `%c'>>\n",'A'+col);
2126 	    fprintf(f,"    >>> # end of Cell\n");
2127 	}
2128 	fprintf(f,"   > # end of Row\n");
2129 	fprintf(f,"  > # end of TblH\n");
2130 	fprintf(f,"  <TblBody # The body\n");
2131     }
2132 
2133     for (row=r0; row<=rn; row++) {
2134 	if ( tbl_style == TEX )
2135 	    (void) fprintf (f, "\\+");
2136 	else if (tbl_style == FRAME) {
2137 	    fprintf(f,"   <Row # The next body row\n");
2138 	}
2139 
2140 	for (pp = ATBL(tbl, row, col=c0); col<=cn; col++, pp++) {
2141 	    if ( tbl_style == FRAME ) {
2142 		fprintf(f,"    <Cell <CellContent <Para\n");
2143 		fprintf(f,"       <PgfTag `CellBody'> # in Paragraph Format Catalog\n");
2144 		fprintf(f,"       <ParaLine <String `");
2145 	    }
2146 	    if (*pp) {
2147 		char *s;
2148 		if ((*pp)->flags&is_valid) {
2149 		    if ((*pp)->cellerror) {
2150 			(void) fprintf (f, "%*s",
2151 					fwidth[col],
2152 			((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
2153 		    } else if ((*pp)->format) {
2154 		        char field[FBUFLEN];
2155 			if (*((*pp)->format) == ctl('d')) {
2156 			    time_t v = (time_t) ((*pp)->v);
2157 			    strftime(field, sizeof(field), ((*pp)->format)+1,
2158 				    localtime(&v));
2159 			} else
2160 			    format((*pp)->format, precision[col], (*pp)->v,
2161 				    field, sizeof(field));
2162 			unspecial(f, field, coldelim);
2163 		    } else {
2164 		        char field[FBUFLEN];
2165                         (void) engformat(realfmt[col], fwidth[col],
2166 				precision[col], (*pp) -> v,
2167 				field, sizeof(field));
2168 			unspecial(f, field, coldelim);
2169 		    }
2170 		}
2171 		if ((s = (*pp)->label)) {
2172 	            unspecial(f, s, coldelim);
2173 		}
2174 	    }
2175 	    if (tbl_style == FRAME) {
2176 		fprintf(f, "'>>\n");
2177 		fprintf(f,"    >>> # end of Cell\n");
2178 	    }
2179 	    if (col < cn)
2180 		if (tbl_style != FRAME)
2181 		    (void) fprintf(f,"%c", coldelim);
2182 	}
2183 	if (tbl_style == LATEX) {
2184 		if (row < rn) (void) fprintf (f, "\\\\");
2185 	}
2186 	else if (tbl_style == SLATEX) {
2187 		if (row < rn) (void) fprintf(f, "!!");
2188 	}
2189 	else if (tbl_style == TEX) {
2190 		(void) fprintf (f, "\\cr");
2191 	}
2192 	else if (tbl_style == FRAME) {
2193 	    fprintf(f,"   > # end of Row\n");
2194 	}
2195 	(void) fprintf(f,"\n");
2196     }
2197 
2198     if (tbl_style == TBL)
2199     (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname);
2200     else if (tbl_style == LATEX)
2201 	(void) fprintf(f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname);
2202     else if (tbl_style == SLATEX)
2203 	(void) fprintf (f,"!end<tabular>\n%% ** end of %s spreadsheet output\n", progname);
2204     else if (tbl_style == TEX)
2205 	(void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname);
2206     else if (tbl_style == FRAME) {
2207 	fprintf(f,"  > # end of TblBody\n");
2208 	fprintf(f," ># end of Tbl\n");
2209 	fprintf(f,"> # end of Tbls\n");
2210 	fprintf(f,"<TextFlow <Para \n");
2211 	fprintf(f,"  <PgfTag Body> \n");
2212 	fprintf(f,"  <ParaLine <ATbl 1>> # Reference to table ID 1\n");
2213 	fprintf(f,">>\n");
2214     }
2215 
2216     closefile(f, pid, 0);
2217 }
2218 
2219 /* unspecial (backquote) things that are special chars in a table */
2220 void
2221 unspecial(FILE *f, char *str, int delim)
2222 {
2223     if (*str == '\\') str++; /* delete wheeling string operator, OK? */
2224     while (*str) {
2225     	if (((tbl_style == LATEX) || (tbl_style == SLATEX) ||
2226 		(tbl_style == TEX)) &&
2227 		((*str == delim) || (*str == '$') || (*str == '#') ||
2228 		(*str == '%') || (*str == '{') || (*str == '}') ||
2229 		(*str == '&')))
2230 	    putc('\\', f);
2231 	putc(*str, f);
2232 	str++;
2233     }
2234 }
2235 
2236 struct enode *
2237 copye(register struct enode *e, int Rdelta, int Cdelta, int r1, int c1,
2238 	int r2, int c2, int transpose)
2239 {
2240     register struct enode *ret;
2241     static struct enode *range = NULL;
2242 
2243     if (e == (struct enode *)0)
2244 	ret = (struct enode *)0;
2245     else if (e->op & REDUCE) {
2246 	int newrow, newcol;
2247 	if (freeenodes) {
2248 	    ret = freeenodes;
2249 	    freeenodes = ret->e.o.left;
2250 	} else
2251 	    ret = (struct enode *) scxmalloc((unsigned) sizeof (struct enode));
2252 	ret->op = e->op;
2253 	newrow = e->e.r.left.vf & FIX_ROW ||
2254 		 e->e.r.left.vp->row < r1 || e->e.r.left.vp->row > r2 ||
2255 		 e->e.r.left.vp->col < c1 || e->e.r.left.vp->col > c2 ?
2256 		 e->e.r.left.vp->row :
2257 		 transpose ? r1 + Rdelta + e->e.r.left.vp->col - c1 :
2258 		 e->e.r.left.vp->row + Rdelta;
2259 	newcol = e->e.r.left.vf & FIX_COL ||
2260 		 e->e.r.left.vp->row < r1 || e->e.r.left.vp->row > r2 ||
2261 		 e->e.r.left.vp->col < c1 || e->e.r.left.vp->col > c2 ?
2262 		 e->e.r.left.vp->col :
2263 		 transpose ? c1 + Cdelta + e->e.r.left.vp->row - r1 :
2264 		 e->e.r.left.vp->col + Cdelta;
2265 	ret->e.r.left.vp = lookat(newrow, newcol);
2266 	ret->e.r.left.vf = e->e.r.left.vf;
2267 	newrow = e->e.r.right.vf & FIX_ROW ||
2268 		 e->e.r.right.vp->row < r1 || e->e.r.right.vp->row > r2 ||
2269 		 e->e.r.right.vp->col < c1 || e->e.r.right.vp->col > c2 ?
2270 		 e->e.r.right.vp->row :
2271 		 transpose ? r1 + Rdelta + e->e.r.right.vp->col - c1 :
2272 		 e->e.r.right.vp->row + Rdelta;
2273 	newcol = e->e.r.right.vf & FIX_COL ||
2274 		 e->e.r.right.vp->row < r1 || e->e.r.right.vp->row > r2 ||
2275 		 e->e.r.right.vp->col < c1 || e->e.r.right.vp->col > c2 ?
2276 		 e->e.r.right.vp->col :
2277 		 transpose ? c1 + Cdelta + e->e.r.right.vp->row - r1 :
2278 		 e->e.r.right.vp->col + Cdelta;
2279 	ret->e.r.right.vp = lookat(newrow, newcol);
2280 	ret->e.r.right.vf = e->e.r.right.vf;
2281     } else {
2282 	struct enode *temprange;
2283 
2284 	if (freeenodes) {
2285 	    ret = freeenodes;
2286 	    freeenodes = ret->e.o.left;
2287 	} else
2288 	    ret = (struct enode *) scxmalloc((unsigned) sizeof (struct enode));
2289 	ret->op = e->op;
2290 	switch (ret->op) {
2291 	    case SUM:
2292 	    case PROD:
2293 	    case AVG:
2294 	    case COUNT:
2295 	    case STDDEV:
2296 	    case MAX:
2297 	    case MIN:
2298 		temprange = range;
2299 		range = e->e.o.left;
2300 		r1 = 0;
2301 		c1 = 0;
2302 		r2 = maxrow;
2303 		c2 = maxcol;
2304 	}
2305 	switch (ret->op) {
2306 	    case 'v':
2307 		{
2308 		    int newrow, newcol;
2309 		    if (range && e->e.v.vp->row >= range->e.r.left.vp->row &&
2310 				 e->e.v.vp->row <= range->e.r.right.vp->row &&
2311 				 e->e.v.vp->col >= range->e.r.left.vp->col &&
2312 				 e->e.v.vp->col <= range->e.r.right.vp->col) {
2313 			newrow = range->e.r.left.vf & FIX_ROW ?
2314 				e->e.v.vp->row : e->e.v.vp->row + Rdelta;
2315 			newcol = range->e.r.left.vf & FIX_COL ?
2316 				e->e.v.vp->col : e->e.v.vp->col + Cdelta;
2317 		    } else {
2318 			newrow = e->e.v.vf & FIX_ROW ||
2319 				 e->e.v.vp->row < r1 || e->e.v.vp->row > r2 ||
2320 				 e->e.v.vp->col < c1 || e->e.v.vp->col > c2 ?
2321 				 e->e.v.vp->row :
2322 				 transpose ? r1 + Rdelta + e->e.v.vp->col - c1 :
2323 				 e->e.v.vp->row + Rdelta;
2324 			newcol = e->e.v.vf & FIX_COL ||
2325 				 e->e.v.vp->row < r1 || e->e.v.vp->row > r2 ||
2326 				 e->e.v.vp->col < c1 || e->e.v.vp->col > c2 ?
2327 				 e->e.v.vp->col :
2328 				 transpose ? c1 + Cdelta + e->e.v.vp->row - r1 :
2329 				 e->e.v.vp->col + Cdelta;
2330 		    }
2331 		    ret->e.v.vp = lookat(newrow, newcol);
2332 		    ret->e.v.vf = e->e.v.vf;
2333 		    break;
2334 		}
2335 	    case 'k':
2336 		ret->e.k = e->e.k;
2337 		break;
2338 	    case 'f':
2339 	    case 'F':
2340 		if (range && ret->op == 'F' ||
2341 			!range && ret->op == 'f')
2342 		    Rdelta = Cdelta = 0;
2343 		ret->e.o.left = copye(e->e.o.left, Rdelta, Cdelta,
2344 			r1, c1, r2, c2, transpose);
2345 		ret->e.o.right = (struct enode *)0;
2346  		break;
2347 	    case '$':
2348 	    case EXT:
2349 		ret->e.s = scxmalloc((unsigned) strlen(e->e.s)+1);
2350 		(void) strcpy(ret->e.s, e->e.s);
2351 		if (e->op == '$')	/* Drop through if ret->op is EXT */
2352 		    break;
2353 	    default:
2354 		ret->e.o.left = copye(e->e.o.left, Rdelta, Cdelta,
2355 			r1, c1, r2, c2, transpose);
2356 		ret->e.o.right = copye(e->e.o.right, Rdelta, Cdelta,
2357 			r1, c1, r2, c2, transpose);
2358 		break;
2359 	}
2360 	switch (ret->op) {
2361 	    case SUM:
2362 	    case PROD:
2363 	    case AVG:
2364 	    case COUNT:
2365 	    case STDDEV:
2366 	    case MAX:
2367 	    case MIN:
2368 		range = temprange;
2369 	}
2370     }
2371     return ret;
2372 }
2373 
2374 /*
2375  * sync_refs and syncref are used to remove references to
2376  * deleted struct ents.  Note that the deleted structure must still
2377  * be hanging around before the call, but not referenced by an entry
2378  * in tbl.  Thus the free_ent calls in sc.c
2379  */
2380 void
2381 sync_refs()
2382 {
2383     int i, j;
2384     register struct ent *p;
2385     sync_ranges();
2386     for (i=0; i<=maxrow; i++)
2387 	for (j=0; j<=maxcol; j++)
2388 	    if ((p = *ATBL(tbl, i, j)) && p->expr)
2389 		syncref(p->expr);
2390     for (i = 0; i < DELBUFSIZE; i++) {
2391 	p = delbuf[i];
2392 	while (p && p->expr) {
2393 	    syncref(p->expr);
2394 	    p = p->next;
2395 	}
2396     }
2397 }
2398 
2399 void
2400 syncref(register struct enode *e)
2401 {
2402     if (e == (struct enode *)0)
2403 	return;
2404     else if (e->op & REDUCE) {
2405  	e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col);
2406  	e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col);
2407     } else {
2408 	switch (e->op) {
2409 	    case 'v':
2410 		if (e->e.v.vp->flags & is_cleared) {
2411 		    e->op = ERR_;
2412 		    e->e.o.left = NULL;
2413 		    e->e.o.right = NULL;
2414 		} else if (e->e.v.vp->flags & may_sync)
2415 		    e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col);
2416 		break;
2417 	    case 'k':
2418 		break;
2419 	    case '$':
2420 		break;
2421 	    default:
2422 		syncref(e->e.o.right);
2423 		syncref(e->e.o.left);
2424 		break;
2425 	}
2426     }
2427 }
2428 
2429 /* mark a row as hidden */
2430 void
2431 hiderow(int arg)
2432 {
2433     register int r1;
2434     register int r2;
2435 
2436     r1 = currow;
2437     r2 = r1 + arg - 1;
2438     if (r1 < 0 || r1 > r2) {
2439 	error("Invalid range");
2440 	return;
2441     }
2442     if (r2 >= maxrows-1) {
2443     	if (!growtbl(GROWROW, arg+1, 0)) {
2444 	    error("You can't hide the last row");
2445 	    return;
2446 	}
2447     }
2448     FullUpdate++;
2449     modflg++;
2450     while (r1 <= r2)
2451 	row_hidden[r1++] = 1;
2452 }
2453 
2454 /* mark a column as hidden */
2455 void
2456 hidecol(int arg)
2457 {
2458     int c1;
2459     int c2;
2460 
2461     c1 = curcol;
2462     c2 = c1 + arg - 1;
2463     if (c1 < 0 || c1 > c2) {
2464 	error ("Invalid range");
2465 	return;
2466     }
2467     if (c2 >= maxcols-1) {
2468     	if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1)) {
2469 	    error("You can't hide the last col");
2470 	    return;
2471 	}
2472     }
2473     FullUpdate++;
2474     modflg++;
2475     while (c1 <= c2)
2476 	col_hidden[c1++] = TRUE;
2477 }
2478 
2479 /* mark a row as not-hidden */
2480 void
2481 showrow(int r1, int r2)
2482 {
2483     if (r1 < 0 || r1 > r2) {
2484 	error ("Invalid range");
2485 	return;
2486     }
2487     if (r2 > maxrows-1) {
2488 	r2 = maxrows-1;
2489     }
2490     FullUpdate++;
2491     modflg++;
2492     while (r1 <= r2)
2493 	row_hidden[r1++] = 0;
2494 }
2495 
2496 /* mark a column as not-hidden */
2497 void
2498 showcol(int c1, int c2)
2499 {
2500     if (c1 < 0 || c1 > c2) {
2501 	error ("Invalid range");
2502 	return;
2503     }
2504     if (c2 > maxcols-1) {
2505 	c2 = maxcols-1;
2506     }
2507     FullUpdate++;
2508     modflg++;
2509     while (c1 <= c2)
2510 	col_hidden[c1++] = FALSE;
2511 }
2512 
2513 /* Open the input or output file, setting up a pipe if needed */
2514 FILE *
2515 openfile(char *fname, int *rpid, int *rfd)
2516 {
2517     int pipefd[4];
2518     int pid;
2519     FILE *f;
2520     char *efname;
2521 
2522     while (*fname && (*fname == ' '))	/* Skip leading blanks */
2523 	fname++;
2524 
2525     if (*fname != '|') {		/* Open file if not pipe */
2526 	*rpid = 0;
2527 	if (rfd != NULL)
2528 	    *rfd = 1;			/* Set to stdout just in case */
2529 
2530 	efname = findhome(fname);
2531 #ifdef DOBACKUPS
2532 	if (rfd == NULL && !backup_file(efname) &&
2533 	    (yn_ask("Could not create backup copy.  Save anyway?: (y,n)") != 1))
2534 		return (0);
2535 #endif
2536 	return (fopen(efname, rfd == NULL ? "w" : "r"));
2537     }
2538 
2539 #if defined(MSDOS)
2540     error("Piping not available under MS-DOS\n");
2541     return (0);
2542 #else
2543     fname++;				/* Skip | */
2544     efname = findhome(fname);
2545     if (pipe(pipefd) < 0 || (rfd != NULL && pipe(pipefd+2) < 0)) {
2546 	error("Can't make pipe to child");
2547 	*rpid = 0;
2548 	return (0);
2549     }
2550 
2551     deraw(rfd==NULL);
2552 #ifdef VMS
2553     fprintf(stderr, "No son tasks available yet under VMS--sorry\n");
2554 #else /* VMS */
2555 
2556     if ((pid=fork()) == 0) {		/* if child */
2557 	(void) close(0);		/* close stdin */
2558 	(void) close(pipefd[1]);
2559 	(void) dup(pipefd[0]);		/* connect to first pipe */
2560 	if (rfd != NULL) {		/* if opening for read */
2561 	    (void) close(1);		/* close stdout */
2562 	    (void) close(pipefd[2]);
2563 	    (void) dup(pipefd[3]);	/* connect to second pipe */
2564 	}
2565 	(void) signal(SIGINT, SIG_DFL);	/* reset */
2566 	(void) execl("/bin/sh", "sh", "-c", efname, 0);
2567 	exit (-127);
2568     } else {				/* else parent */
2569 	*rpid = pid;
2570 	if ((f = fdopen(pipefd[(rfd==NULL?1:2)], rfd==NULL?"w":"r")) == NULL) {
2571 	    (void) kill(pid, 9);
2572 	    error("Can't fdopen %sput", rfd==NULL?"out":"in");
2573 	    (void) close(pipefd[1]);
2574 	    if (rfd != NULL)
2575 		(void) close(pipefd[3]);
2576 	    *rpid = 0;
2577 	    return (0);
2578 	}
2579     }
2580     (void) close(pipefd[0]);
2581     if (rfd != NULL) {
2582 	(void) close(pipefd[3]);
2583 	*rfd = pipefd[1];
2584     }
2585 #endif /* VMS */
2586     return (f);
2587 #endif /* MSDOS */
2588 }
2589 
2590 /* close a file opened by openfile(), if process wait for return */
2591 void
2592 closefile(FILE *f, int pid, int rfd)
2593 {
2594     int temp;
2595 
2596     (void) fclose(f);
2597 #ifndef MSDOS
2598     if (pid) {
2599 	while (pid != wait(&temp)) /**/;
2600 	if (rfd==0) {
2601 	    (void) printf("Press any key to continue ");
2602 	    (void) fflush(stdout);
2603 	    cbreak();
2604 	    (void) nmgetch();
2605 	    goraw();
2606 	    clear();
2607 	} else {
2608 	    close(rfd);
2609 	    if (usecurses) {
2610 #ifdef VMS
2611 		VMS_read_raw = 1;
2612 #else /* VMS */
2613 #if SYSV2 || SYSV3
2614 		fixterm();
2615 #else /* SYSV2 || SYSV3 */
2616 		cbreak();
2617 		nonl();
2618 		noecho ();
2619 #endif /* SYSV2 || SYSV3 */
2620 		kbd_again();
2621 #endif /* VMS */
2622 		if (color && has_colors())
2623 		    bkgdset(COLOR_PAIR(1) | ' ');
2624 	    }
2625 	}
2626     }
2627 #endif /* MSDOS */
2628     if (brokenpipe) {
2629 	error("Broken pipe");
2630 	brokenpipe = FALSE;
2631     }
2632 }
2633 
2634 /* Copy a cell (struct ent).  "special" indicates special treatment when
2635  * merging two cells for the "pm" command, merging formats only for the
2636  * "pf" command, or for adjusting cell references when transposing with
2637  * the "pt" command.  r1, c1, r2, and c2 define the range in which the dr
2638  * and dc values should be used.
2639  */
2640 void
2641 copyent(register struct ent *n, register struct ent *p, int dr, int dc,
2642 	int r1, int c1, int r2, int c2, int special)
2643 {
2644     if (!n || !p) {
2645 	error("internal error");
2646 	return;
2647     }
2648     if (special != 'f') {
2649 	if (special != 'm' || p->flags & is_valid) {
2650 	    n->v = p->v;
2651 	    n->flags |= p->flags & is_valid;
2652 	}
2653 	if (special != 'm' || p->expr) {
2654 	    n->expr = copye(p->expr, dr, dc, r1, c1, r2, c2, special == 't');
2655 	    if (p->flags & is_strexpr)
2656 		n->flags |= is_strexpr;
2657 	    else
2658 		n->flags &= ~is_strexpr;
2659 	}
2660 	if (p->label) {
2661 	    if (n->label)
2662 		scxfree(n->label);
2663 	    n->label = scxmalloc((unsigned) (strlen(p->label) + 1));
2664 	    (void) strcpy(n->label, p->label);
2665 	    n->flags &= ~is_leftflush;
2666 	    n->flags |= ((p->flags & is_label) | (p->flags & is_leftflush));
2667 	} else if (special != 'm') {
2668 	    n->label = NULL;
2669 	    n->flags &= ~(is_label | is_leftflush);
2670 	}
2671 	n->flags |= p->flags & is_locked;
2672     }
2673     if (p->format) {
2674 	if (n->format)
2675 	    scxfree(n->format);
2676         n->format = scxmalloc((unsigned) (strlen(p->format) + 1));
2677 	(void) strcpy(n->format, p->format);
2678     } else if (special != 'm' && special != 'f')
2679 	n->format = NULL;
2680     n->flags |= is_changed;
2681 }
2682 
2683 #ifndef MSDOS
2684 /* add a plugin/mapping pair to the end of the filter list. type is
2685  * r(ead) or w(rite)
2686  */
2687 
2688 void
2689 addplugin(char *ext, char *plugin, char type)
2690 {
2691     struct impexfilt *fp;
2692     char mesg[PATHLEN];
2693 
2694     if (!plugin_exists(plugin, strlen(plugin), mesg)) {
2695 	error("Cannot find plugin %s", plugin);
2696 	return;
2697     }
2698     if (filt == NULL) {
2699 	filt = (struct impexfilt *) scxmalloc((unsigned)sizeof(struct impexfilt));
2700 	fp = filt;
2701     } else {
2702 	fp = filt;
2703 	while (fp->next != NULL)
2704 	    fp = fp->next;
2705 	fp->next = (struct impexfilt *)scxmalloc((unsigned)sizeof(struct impexfilt));
2706 	fp = fp->next;
2707     }
2708     strcpy(fp->plugin, plugin);
2709     strcpy(fp->ext, ext);
2710     fp->type = type;
2711     fp->next = NULL;
2712 }
2713 
2714 char *
2715 findplugin(char *ext, char type)
2716 {
2717     struct impexfilt *fp;
2718 
2719     fp = filt;
2720     if (fp == NULL)
2721 	return (NULL);
2722     if ((!strcmp(fp->ext, ext)) && (fp->type == type))
2723 	return (fp->plugin);
2724     while (fp->next != NULL) {
2725 	fp = fp->next;
2726 	if ((!strcmp(fp->ext, ext)) && (fp->type == type))
2727 	    return (fp->plugin);
2728     }
2729 
2730     return (NULL);
2731 }
2732 #endif
2733 
2734 void
2735 write_fd(register FILE *f, int r0, int c0, int rn, int cn)
2736 {
2737     register struct ent **pp;
2738     int r, c;
2739 
2740     (void) fprintf(f, "# This data file was generated by the Spreadsheet ");
2741     (void) fprintf(f, "Calculator.\n");
2742     (void) fprintf(f, "# You almost certainly shouldn't edit it.\n\n");
2743     print_options(f);
2744     for (c = 0; c < COLFORMATS; c++)
2745 	if (colformat[c])
2746 	    (void) fprintf (f, "format %d = \"%s\"\n", c, colformat[c]);
2747     for (c = c0; c <= cn; c++)
2748 	if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC ||
2749 		realfmt[c] != DEFREFMT)
2750 	    (void) fprintf (f, "format %s %d %d %d\n", coltoa(c), fwidth[c],
2751 		    precision[c], realfmt[c]);
2752     for (c = c0; c <= cn; c++)
2753         if (col_hidden[c])
2754             (void) fprintf(f, "hide %s\n", coltoa(c));
2755     for (r = r0; r <= rn; r++)
2756 	if (row_hidden[r])
2757 	    (void) fprintf(f, "hide %d\n", r);
2758 
2759     write_ranges(f);
2760     write_franges(f);
2761     write_colors(f, 0);
2762     write_cranges(f);
2763 
2764     if (mdir)
2765 	(void) fprintf(f, "mdir \"%s\"\n", mdir);
2766     if (autorun)
2767 	(void) fprintf(f, "autorun \"%s\"\n", autorun);
2768     for (c = 0; c < FKEYS; c++)
2769 	if (fkey[c])
2770 	    (void) fprintf(f, "fkey %d = \"%s\"\n", c + 1, fkey[c]);
2771 
2772     write_cells(f, r0, c0, rn, cn, r0, c0);
2773     for (r = r0; r <= rn; r++) {
2774 	pp = ATBL(tbl, r, c0);
2775 	for (c = c0; c <= cn; c++, pp++)
2776 	    if (*pp) {
2777 		if ((*pp)->flags&is_locked)
2778 		    (void) fprintf(f, "lock %s%d\n", coltoa((*pp)->col),
2779 						     (*pp)->row);
2780 		if ((*pp)->nrow >= 0) {
2781 		    (void) fprintf(f, "addnote %s ",
2782 			    v_name((*pp)->row, (*pp)->col));
2783 		    (void) fprintf(f, "%s\n", r_name((*pp)->nrow, (*pp)->ncol,
2784 			    (*pp)->nlastrow, (*pp)->nlastcol));
2785 		}
2786 	    }
2787     }
2788     /*
2789      * Don't try to combine these into a single fprintf().  v_name() has
2790      * a single buffer that is overwritten on each call, so the first part
2791      * needs to be written to the file before making the second call.
2792      */
2793     fprintf(f, "goto %s", v_name(currow, curcol));
2794     fprintf(f, " %s\n", v_name(strow, stcol));
2795 }
2796 
2797 void
2798 write_cells(register FILE *f, int r0, int c0, int rn, int cn, int dr, int dc)
2799 {
2800     register struct ent **pp;
2801     int r, c, rs, cs, mf;
2802     char *dpointptr;
2803 
2804     mf = modflg;
2805     if (dr != r0 || dc != c0) {
2806 	yank_area(r0, c0, rn, cn);
2807 	rn += dr - r0;
2808 	cn += dc - c0;
2809 	rs = currow;
2810 	cs = curcol;
2811 	currow = dr;
2812 	curcol = dc;
2813 	pullcells('x');
2814     }
2815     if (Vopt) valueize_area(dr, dc, rn, cn);
2816     for (r = dr; r <= rn; r++) {
2817 	pp = ATBL(tbl, r, dc);
2818 	for (c = dc; c <= cn; c++, pp++)
2819 	    if (*pp) {
2820 		if ((*pp)->label || (*pp)->flags & is_strexpr) {
2821 		    edits(r, c);
2822 		    (void) fprintf(f, "%s\n", line);
2823 		}
2824 		if ((*pp)->flags & is_valid) {
2825 		    editv(r, c);
2826 		    dpointptr = strchr(line, dpoint);
2827 		    if (dpointptr != NULL)
2828 			*dpointptr = '.';
2829 		    (void) fprintf(f, "%s\n", line);
2830 		}
2831 		if ((*pp)->format) {
2832 		    editfmt(r, c);
2833 		    (void) fprintf(f, "%s\n",line);
2834 		}
2835 	    }
2836     }
2837     if (dr != r0 || dc != c0) {
2838 	pullcells('x');
2839 	currow = rs;
2840 	curcol = cs;
2841 	flush_saved();
2842     }
2843     modflg = mf;
2844 }
2845 
2846 int
2847 writefile(char *fname, int r0, int c0, int rn, int cn)
2848 {
2849     register FILE *f;
2850     char save[PATHLEN];
2851     char tfname[PATHLEN];
2852     long namelen;
2853     char *tpp;
2854     char *p;
2855     char *plugin;
2856     int pid;
2857 
2858 #ifndef MSDOS
2859     /* find the extension and mapped plugin if exists */
2860     if ((p = strrchr(fname, '.'))) {
2861 	if ((plugin = findplugin(p+1, 'w')) != NULL) {
2862 	    if (!plugin_exists(plugin, strlen(plugin), save + 1)) {
2863 		error("plugin not found");
2864 		return (-1);
2865 	    }
2866 	    *save = '|';
2867 	    if ((strlen(save) + strlen(fname) + 20) > PATHLEN) {
2868 		error("Path too long");
2869 		return (-1);
2870 	    }
2871 	    sprintf(save + strlen(save), " %s%d:", coltoa(c0), r0);
2872 	    sprintf(save + strlen(save), "%s%d \"%s\"", coltoa(cn), rn, fname);
2873 	    /* pass it to readfile as an advanced macro */
2874 	    readfile(save, 0);
2875 	    return (0);
2876 	}
2877     }
2878 #endif
2879 
2880 #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH)
2881     if (Crypt) {
2882 	return (cwritefile(fname, r0, c0, rn, cn));
2883     }
2884 #endif /* VMS */
2885 
2886     if (*fname == '\0')
2887 	if (isatty(STDOUT_FILENO) || *curfile != '\0')
2888 	    fname = curfile;
2889 	else {
2890 	    write_fd(stdout, r0, c0, rn, cn);
2891 	    return (0);
2892 	}
2893 
2894 #ifdef MSDOS
2895     namelen = 12;
2896 #else
2897     if ((tpp = strrchr(fname, '/')) == NULL)
2898 	namelen = pathconf(".", _PC_NAME_MAX);
2899     else {
2900 	*tpp = '\0';
2901 	namelen = pathconf(fname, _PC_NAME_MAX);
2902 	*tpp = '/';
2903     }
2904 #endif /* MSDOS */
2905 
2906     (void) strcpy(tfname, fname);
2907     for (tpp = tfname; *tpp != '\0'; tpp++)
2908 	if (*tpp == '\\' && *(tpp + 1) == '"')
2909 	    (void) memmove(tpp, tpp + 1, strlen(tpp));
2910 
2911     if (scext != NULL) {
2912 	if (strlen(tfname) > 3 && !strcmp(tfname + strlen(tfname) - 3, ".sc"))
2913 	    tfname[strlen(tfname) - 3] = '\0';
2914 	else if (strlen(tfname) > strlen(scext) + 1 &&
2915 		tfname[strlen(tfname) - strlen(scext) - 1] == '.' &&
2916 		!strcmp(tfname + strlen(tfname) - strlen(scext), scext))
2917 	    tfname[strlen(tfname) - strlen(scext) - 1] = '\0';
2918 	tfname[namelen - strlen(scext) - 1] = '\0';
2919 	strcat(tfname, ".");
2920 	strcat(tfname, scext);
2921     }
2922 
2923     (void) strcpy(save, tfname);
2924     for (tpp = save; *tpp != '\0'; tpp++)
2925 	if (*tpp == '"') {
2926 	    (void) memmove(tpp + 1, tpp, strlen(tpp) + 1);
2927 	    *tpp++ = '\\';
2928 	}
2929 
2930     if ((f = openfile(tfname, &pid, NULL)) == NULL) {
2931 	error("Can't create file \"%s\"", save);
2932 	return (-1);
2933     }
2934 
2935     if (usecurses) {
2936 	error("Writing file \"%s\"...", save);
2937 	refresh();
2938     }
2939     write_fd(f, r0, c0, rn, cn);
2940 
2941     closefile(f, pid, 0);
2942 
2943     if (!pid) {
2944         (void) strcpy(curfile, save);
2945         modflg = 0;
2946 	FullUpdate++;
2947 	if (usecurses)
2948 	    error("File \"%s\" written", curfile);
2949 	else
2950 	    fprintf(stderr, "\nFile \"%s\" written", curfile);
2951     }
2952 
2953     return (0);
2954 }
2955 
2956 int
2957 readfile(char *fname, int eraseflg)
2958 {
2959     register FILE *f;
2960     char save[PATHLEN];
2961     int tempautolabel;
2962     char *p;
2963     char *plugin;
2964     int pid = 0;
2965     int rfd = STDOUT_FILENO, savefd;
2966 
2967     tempautolabel = autolabel;		/* turn off auto label when */
2968     autolabel = 0;			/* reading a file */
2969 
2970     if (*fname == '*' && mdir) {
2971        (void) strcpy(save, mdir);
2972        (void) strcat(save, fname);
2973     } else {
2974         if (*fname == '\0')
2975 	    fname = curfile;
2976 	(void) strcpy(save, fname);
2977     }
2978 
2979 #ifndef MSDOS
2980     if ((p = strrchr(fname, '.')) && (fname[0] != '|')) {  /* exclude macros */
2981 	if ((plugin = findplugin(p+1, 'r')) != NULL) {
2982 	    if (!(plugin_exists(plugin, strlen(plugin), save + 1))) {
2983 		error("plugin not found");
2984 		return (-1);
2985 	    }
2986 	    *save = '|';
2987 	    if ((strlen(save) + strlen(fname) + 2) > PATHLEN) {
2988 		error("Path too long");
2989 		return (-1);
2990 	    }
2991 	    sprintf(save + strlen(save), " \"%s\"", fname);
2992 	    eraseflg = 0;
2993 	    /* get filename: could be preceded by params if this is
2994 	    * a save
2995 	    */
2996 	    while (p > fname) {
2997 		if (*p == ' ') {
2998 		    p++;
2999 		    break;
3000 		}
3001 		p--;
3002 	    }
3003 	    (void) strcpy(curfile, p);
3004 	}
3005     }
3006 #endif
3007 
3008 #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH)
3009     if (Crypt) {
3010 	int ret = 0;
3011 	if (*save == '-' && strlen(fname) == 1)
3012 	    error("Can't use encryption in a pipeline.");
3013 	else
3014 	    if (*save == '|')
3015 		error("Can't use encryption with advanced macros.");
3016 	else
3017 	    ret = creadfile(save, eraseflg);
3018 	autolabel = tempautolabel;
3019 	return (ret);
3020     }
3021 #endif /* VMS */
3022 
3023     if (eraseflg && strcmp(fname, curfile) && modcheck(" first")) return 0;
3024 
3025 #ifndef MSDOS
3026     if (fname[0] == '-' && fname[1] == '\0') {
3027 	f = stdin;
3028 	*save = '\0';
3029     } else {
3030 #endif /* MSDOS */
3031 	if ((f = openfile(save, &pid, &rfd)) == NULL) {
3032 	    error("Can't read file \"%s\"", save);
3033 	    autolabel = tempautolabel;
3034 	    return 0;
3035 	} else if (eraseflg) {
3036 	    if (usecurses) {
3037 		error("Reading file \"%s\"", save);
3038 		refresh();
3039 	    } else
3040 		fprintf(stderr, "\nReading file \"%s\"", save);
3041 	}
3042 #ifndef MSDOS
3043     }
3044     if (*fname == '|')
3045 	*save = '\0';
3046 #endif /* MSDOS */
3047 
3048     if (eraseflg) erasedb();
3049 
3050     remember(0);
3051     loading++;
3052     savefd = macrofd;
3053     macrofd = rfd;
3054     while (!brokenpipe && fgets(line, sizeof(line), f)) {
3055 #ifndef MSDOS
3056 	if (line[0] == '|' && pid != 0) {
3057 	    line[0] = ' ';
3058 	}
3059 #endif /* MSDOS */
3060 	linelim = 0;
3061 	if (line[0] != '#') (void) yyparse();
3062     }
3063     macrofd = savefd;
3064     --loading;
3065     remember(1);
3066     closefile(f, pid, rfd);
3067 #ifndef MSDOS
3068     if (f == stdin) {
3069 	freopen("/dev/tty", "r", stdin);
3070 	goraw();
3071     }
3072 #endif /* MSDOS */
3073     linelim = -1;
3074     if (eraseflg) {
3075 	(void) strcpy(curfile, save);
3076 	modflg = 0;
3077 	cellassign = 0;
3078 	if (autorun && !skipautorun) (void) readfile(autorun, 0);
3079 	skipautorun = 0;
3080 	EvalAll();
3081     }
3082     autolabel = tempautolabel;
3083     FullUpdate++;
3084     return 1;
3085 }
3086 
3087 /* erase the database (tbl, etc.) */
3088 void
3089 erasedb()
3090 {
3091     int  r, c;
3092     char *home;
3093 
3094     for (c = 0; c<=maxcol; c++) {
3095 	fwidth[c] = DEFWIDTH;
3096 	precision[c] = DEFPREC;
3097 	realfmt[c] = DEFREFMT;
3098     }
3099 
3100     for (r = 0; r<=maxrow; r++) {
3101 	register struct ent **pp = ATBL(tbl, r, 0);
3102 	for (c=0; c++<=maxcol; pp++)
3103 	    if (*pp) {
3104 		if ((*pp)->expr)  efree((*pp) -> expr);
3105 		if ((*pp)->label) scxfree((char *)((*pp) -> label));
3106 		(*pp)->next = freeents;	/* save [struct ent] for reuse */
3107 		freeents = *pp;
3108 		*pp = (struct ent *)0;
3109 	    }
3110     }
3111 
3112     for (c = 0; c < COLFORMATS; c++)
3113 	if (colformat[c]) {
3114 	    scxfree(colformat[c]);
3115 	    colformat[c] = NULL;
3116 	}
3117 
3118     maxrow = 0;
3119     maxcol = 0;
3120     clean_range();
3121     clean_frange();
3122     clean_crange();
3123 
3124     propagation = 10;
3125     calc_order = BYROWS;
3126     prescale = 1.0;
3127     tbl_style = 0;
3128     craction = 0;
3129     rowlimit = collimit = -1;
3130     qbuf = 0;
3131 
3132     autocalc=showcell=showtop=1;
3133     autoinsert=autowrap=optimize=numeric=extfunc=color=colorneg=colorerr=cslop=0;
3134     currow=curcol=strow=stcol=0;
3135     if (usecurses && has_colors())
3136 	color_set(0, NULL);
3137     /* unset all marks */
3138     for (c = 1; c < 37; c++)
3139 	savedrow[c] = savedcol[c] = savedstrow[c] = savedstcol[c] = -1;
3140 
3141     if (mdir) {
3142 	scxfree(mdir);
3143 	mdir = NULL;
3144     }
3145     if (autorun) {
3146 	scxfree(autorun);
3147 	autorun = NULL;
3148     }
3149     for (c = 0; c < FKEYS; c++)
3150 	if (fkey[c]) {
3151 	    scxfree(fkey[c]);
3152 	    fkey[c] = NULL;
3153 	}
3154 
3155 #ifndef MSDOS
3156     /*
3157      * Load $HOME/.scrc if present.
3158      */
3159     if ((home = getenv("HOME"))) {
3160 	strcpy(curfile, home);
3161 	strcat(curfile, "/.scrc");
3162 	if ((c = open(curfile, O_RDONLY)) > -1) {
3163 	    close(c);
3164 	    (void) readfile(curfile, 0);
3165 	}
3166     }
3167 
3168     /*
3169      * Load ./.scrc if present and $HOME/.scrc contained `set scrc'.
3170      */
3171     if (scrc && strcmp(home, getcwd(curfile, PATHLEN)) &&
3172 	    (c = open(".scrc", O_RDONLY)) > -1) {
3173 	close(c);
3174 	(void) readfile(".scrc", 0);
3175     }
3176 #endif /* MSDOS */
3177 
3178     *curfile = '\0';
3179     FullUpdate++;
3180 }
3181 
3182 /* moves curcol back one displayed column */
3183 void
3184 backcol(int arg)
3185 {
3186     while (--arg >= 0) {
3187 	if (curcol)
3188 	    curcol--;
3189 	else
3190 	    {error ("At column A"); break;}
3191 	while(col_hidden[curcol] && curcol)
3192 	    curcol--;
3193     }
3194     rowsinrange = 1;
3195     colsinrange = fwidth[curcol];
3196 }
3197 
3198 /* moves curcol forward one displayed column */
3199 void
3200 forwcol(int arg)
3201 {
3202     while (--arg>=0) {
3203 	if (curcol < maxcols - 1)
3204 	    curcol++;
3205 	else
3206 	if (!growtbl(GROWCOL, 0, arg))	/* get as much as needed */
3207 		break;
3208 	else
3209 		curcol++;
3210 	while (col_hidden[curcol] && (curcol < maxcols - 1))
3211 	    curcol++;
3212     }
3213     rowsinrange = 1;
3214     colsinrange = fwidth[curcol];
3215 }
3216 
3217 /* moves currow forward one displayed row */
3218 void
3219 forwrow(int arg)
3220 {
3221     while (--arg>=0) {
3222 	if (currow < maxrows - 1)
3223 	    currow++;
3224 	else
3225 	if (!growtbl(GROWROW, arg, 0))	/* get as much as needed */
3226 		break;
3227 	else
3228 		currow++;
3229 	while (row_hidden[currow]&&(currow<maxrows-1))
3230 	    currow++;
3231     }
3232     rowsinrange = 1;
3233     colsinrange = fwidth[curcol];
3234 }
3235 
3236 /* moves currow backward one displayed row */
3237 void
3238 backrow(int arg)
3239 {
3240     while (--arg>=0) {
3241 	if (currow)
3242 	    currow--;
3243 	else
3244 	    {error("At row zero"); break;}
3245 	while (row_hidden[currow] && currow)
3246 	    currow--;
3247     }
3248     rowsinrange = 1;
3249     colsinrange = fwidth[curcol];
3250 }
3251 
3252 void
3253 markcell()
3254 {
3255     int c;
3256 
3257     error("Mark cell:");
3258     if ((c=nmgetch()) == ESC || c == ctl('g')) {
3259 	error("");
3260 	return;
3261     }
3262     if ((c -= ('a' - 1)) < 1 || c > 26) {
3263 	error("Invalid mark (must be a-z)");
3264 	return;
3265     }
3266     error("");
3267     savedrow[c] = currow;
3268     savedcol[c] = curcol;
3269     savedstrow[c] = strow;
3270     savedstcol[c] = stcol;
3271 }
3272 
3273 void
3274 dotick(int tick)
3275 {
3276     int c;
3277 
3278     remember(0);
3279 
3280     error("Go to marked cell:");
3281     if ((c = nmgetch()) == ESC || c == ctl('g')) {
3282 	error("");
3283 	return;
3284     }
3285     if (c == '`' || c == '\'')
3286 	c = 0;
3287     else if (!(((c -= ('a' - 1)) > 0 && c < 27) ||
3288 	    ((c += ('a' - '0' + 26)) > 26 && c < 37))) {
3289 	error("Invalid mark (must be a-z, 0-9, ` or \')");
3290 	return;
3291     }
3292     if (savedrow[c] == -1) {
3293 	error("Mark not set");
3294 	return;
3295     }
3296     error("");
3297     currow = savedrow[c];
3298     curcol = savedcol[c];
3299     rowsinrange = 1;
3300     colsinrange = fwidth[curcol];
3301     if (tick == '\'') {
3302 	strow = savedstrow[c];
3303 	stcol = savedstcol[c];
3304 	gs.stflag = 1;
3305     } else
3306 	gs.stflag = 0;
3307     remember(1);
3308 
3309     FullUpdate++;
3310 }
3311 
3312 void
3313 gotonote()
3314 {
3315     register struct ent *p;
3316 
3317     p = lookat(currow, curcol);
3318     if (p->nrow == -1)
3319 	error("No note attached");
3320     else
3321 	moveto(p->nrow, p->ncol, p->nlastrow, p->nlastcol, -1, -1);
3322 }
3323 
3324 /*
3325  * Show a cell's label string or expression value.  May overwrite value if
3326  * there is one already displayed in the cell.  Created from old code in
3327  * update(), copied with minimal changes.
3328  */
3329 
3330 void
3331 showstring(char *string,	/* to display */
3332     int dirflush,		/* or rightflush or centered */
3333     int hasvalue,		/* is there a numeric value? */
3334     int row, int col,		/* spreadsheet location */
3335     int *nextcolp,		/* value returned through it */
3336     int mxcol,			/* last column displayed? */
3337     int *fieldlenp,		/* value returned through it */
3338     int r, int c,		/* screen row and column */
3339     struct frange *fr,		/* frame range we're currently in, if any */
3340     int frightcols,		/* number of frame columns to the right */
3341     int flcols, int frcols)	/* width of left and right sides of frame */
3342 {
3343     int nextcol  = *nextcolp;
3344     int fieldlen = *fieldlenp;
3345     char field[FBUFLEN];
3346     int  slen;
3347     char *start, *last;
3348     char *fp, *sp;
3349     struct ent *nc;
3350     struct crange *cr;
3351 
3352     cr = find_crange(row, col);
3353 
3354     /* This figures out if the label is allowed to
3355        slop over into the next blank field */
3356 
3357     slen = strlen(string);
3358     for (sp = string; *sp != '\0'; sp++)
3359 	if (*sp == '\\' && *(sp + 1) == '"')
3360 	    slen--;
3361     if (*string == '\\' && *(string+1) != '\0' && *(string+1) != '"')
3362 	slen = fwidth[col];
3363     if (c + fieldlen == rescol + flcols && nextcol < stcol)
3364 	nextcol = stcol;
3365     if (frightcols &&
3366 	    c + fieldlen + fwidth[nextcol] >= COLS - 1 - frcols &&
3367 	    nextcol < fr->or_right->col - frightcols + 1)
3368 	nextcol = fr->or_right->col - frightcols + 1;
3369     while ((slen > fieldlen) && (nextcol <= mxcol) &&
3370 	    !((nc = lookat(row, nextcol))->flags & is_valid) && !(nc->label) &&
3371 	    (cslop || find_crange(row, nextcol) == cr)) {
3372 
3373 	if (!col_hidden[nextcol])
3374 	    fieldlen += fwidth[nextcol];
3375 	nextcol++;
3376 	if (c + fieldlen == rescol + flcols && nextcol < stcol)
3377 	    nextcol = stcol;
3378 	if (frightcols &&
3379 		c + fieldlen + fwidth[nextcol] >= COLS - 1 - frcols &&
3380 		nextcol < fr->or_right->col - frightcols + 1)
3381 	    nextcol = fr->or_right->col - frightcols + 1;
3382     }
3383     if (slen > fieldlen)
3384 	slen = fieldlen;
3385 
3386     /* Now justify and print */
3387     start = (dirflush&is_leftflush) ? field : field + fieldlen - slen;
3388     if (dirflush & is_label)
3389 	start = field + ((slen<fwidth[col])?(fieldlen-slen)/2:0);
3390     last = field+fieldlen;
3391     fp = field;
3392     if (slen)
3393 	while (fp < start)
3394 	    *fp++ = ' ';
3395     if (*string == '\\' && *(string+1) != '\0' && *(string+1) != '"') {
3396 	char *strt;
3397 	strt = ++string;
3398 
3399 	while (slen--) {
3400 	    if (*string == '\\' && *(string + 1) == '"') {
3401 		slen++;
3402 		string++;
3403 	    } else
3404 		*fp++ = *string++;
3405 	    if (*string == '\0')
3406 		string = strt;
3407 	}
3408     } else
3409 	while (slen--) {
3410 	    if (*string == '\\' && *(string + 1) == '"') {
3411 		slen++;
3412 		string++;
3413 	    } else
3414 		*fp++ = *string++;
3415 	}
3416 
3417     if ((!hasvalue) || fieldlen != fwidth[col])
3418 	while (fp < last)
3419 	    *fp++ = ' ';
3420     *fp = '\0';
3421     for (fp = field; *fp != '\0'; fp++)
3422 	if (*fp == '\\' && *(fp + 1) == '"')
3423 	    memmove(fp, fp + 1, strlen(fp));
3424 #ifdef VMS
3425     mvaddstr(r, c, field);	/* this is a macro */
3426 #else
3427     (void) mvaddstr(r, c, field);
3428 #endif
3429 
3430     *nextcolp  = nextcol;
3431     *fieldlenp = fieldlen;
3432 }
3433 
3434 int
3435 etype(register struct enode *e)
3436 {
3437     if (e == (struct enode *)0)
3438 	return NUM;
3439     switch (e->op) {
3440 	case UPPER: case LOWER: case CAPITAL:
3441 	case O_SCONST: case '#': case DATE: case FMT: case STINDEX:
3442 	case EXT: case SVAL: case SUBSTR:
3443 	    return (STR);
3444 
3445 	case '?':
3446 	case IF:
3447 	    return (etype(e->e.o.right->e.o.left));
3448 
3449 	case 'f':
3450 	    return (etype(e->e.o.right));
3451 
3452 	case O_VAR: {
3453 	    register struct ent *p;
3454 	    p = e->e.v.vp;
3455 	    if (p->expr)
3456 		return (p->flags & is_strexpr ? STR : NUM);
3457 	    else if (p->label)
3458 		return (STR);
3459 	    else
3460 		return (NUM);
3461 	    }
3462 
3463 	default:
3464 	    return (NUM);
3465     }
3466 }
3467 
3468 /* return 1 if yes given, 0 otherwise */
3469 int
3470 yn_ask(char *msg)
3471 {	char ch;
3472 
3473 	(void) move(0, 0);
3474 	(void) clrtoeol();
3475 	(void) addstr(msg);
3476 	(void) refresh();
3477 	while ((ch = nmgetch()) != 'y' && ch != 'Y' && ch != 'n' && ch != 'N') {
3478 	    if (ch == ctl('g') || ch == ESC)
3479 		return (-1);
3480 	}
3481 	if (ch == 'y' || ch == 'Y')
3482 	    return (1);
3483 	else
3484 	    return (0);
3485 }
3486 
3487 /* expand a ~ in a path to your home directory */
3488 #if !defined(MSDOS) && !defined(VMS)
3489 #include <pwd.h>
3490 #endif
3491 char *
3492 findhome(char *path)
3493 {
3494     static	char	*HomeDir = NULL;
3495 
3496     if (*path == '~') {
3497     	char	*pathptr;
3498 	char	tmppath[PATHLEN];
3499 
3500 	if (HomeDir == NULL) {
3501 	    HomeDir = getenv("HOME");
3502 	    if (HomeDir == NULL)
3503 		HomeDir = "/";
3504 	}
3505 	pathptr = path + 1;
3506 	if ((*pathptr == '/') || (*pathptr == '\0'))
3507 	    strcpy(tmppath, HomeDir);
3508 #if !defined(MSDOS) && !defined(VMS)
3509 	else {
3510 	    struct	passwd *pwent;
3511 	    char	*namep;
3512 	    char	name[50];
3513 
3514 	    namep = name;
3515 	    while ((*pathptr != '\0') && (*pathptr != '/'))
3516 		    *(namep++) = *(pathptr++);
3517 	    *namep = '\0';
3518 	    if ((pwent = getpwnam(name)) == NULL) {
3519 	    	(void) sprintf(path, "Can't find user %s", name);
3520 		return (NULL);
3521 	    }
3522 	    strcpy(tmppath, pwent->pw_dir);
3523 	}
3524 #endif
3525 	strcat(tmppath, pathptr);
3526 	strcpy(path, tmppath);
3527     }
3528     return (path);
3529 }
3530 
3531 #ifdef DOBACKUPS
3532 #include <sys/stat.h>
3533 
3534 /*
3535  * make a backup copy of a file, use the same mode and name in the format
3536  * [path/]file~
3537  * return 1 if we were successful, 0 otherwise
3538  */
3539 int
3540 backup_file(char *path)
3541 {
3542     struct	stat	statbuf;
3543     struct	utimbuf	timebuf;
3544     char	fname[PATHLEN];
3545     char	tpath[PATHLEN];
3546 #ifdef sequent
3547     static	char	*buf = NULL;
3548     static	unsigned buflen = 0;
3549 #else
3550     char	buf[BUFSIZ];
3551 #endif
3552     char	*tpp;
3553     int		infd, outfd;
3554     int		count;
3555     mode_t	oldumask;
3556 
3557     /* tpath will be the [path/]file ---> [path/]file~ */
3558     strcpy(tpath, path);
3559     if ((tpp = strrchr(tpath, '/')) == NULL)
3560 	tpp = tpath;
3561     else
3562 	tpp++;
3563     strcpy(fname, tpp);
3564     (void) sprintf(tpp, "%s~", fname);
3565 
3566     if (stat(path, &statbuf) == 0) {
3567 #ifdef sequent
3568 	/* if we know the optimum block size, use it */
3569 	if ((statbuf.st_blksize > buflen) || (buf == NULL)) {
3570 	    buflen = statbuf.st_blksize;
3571 	    if ((buf = scxrealloc(buf, buflen)) == (char *)0) {
3572 		buflen = 0;
3573 		return (0);
3574 	    }
3575 	}
3576 #endif
3577 
3578 	if ((infd = open(path, O_RDONLY, 0)) < 0)
3579 		return (0);
3580 
3581 	oldumask = umask(0);
3582 	outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT, statbuf.st_mode);
3583 	umask(oldumask);
3584 	if (outfd < 0)
3585 	    return (0);
3586 	chown(tpath, statbuf.st_uid, statbuf.st_gid);
3587 
3588 #ifdef sequent
3589 	while ((count = read(infd, buf, statbuf.st_blksize)) > 0) {
3590 #else
3591 	while ((count = read(infd, buf, sizeof(buf))) > 0) {
3592 #endif
3593 	    if (write(outfd, buf, count) != count) {
3594 		count = -1;
3595 		break;
3596 	    }
3597 	}
3598 	close(infd);
3599 	close(outfd);
3600 
3601 	/* copy access and modification times from original file */
3602 	timebuf.actime = statbuf.st_atime;
3603 	timebuf.modtime = statbuf.st_mtime;
3604 	utime(tpath, &timebuf);
3605 
3606 	return ((count < 0) ? 0 : 1);
3607     } else if (errno == ENOENT)
3608 	return (1);
3609     return (0);
3610 }
3611 #endif
3612