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