1 /*
2 * $Id: basic.c,v 1.20 2000/07/22 06:13:15 danny Exp $
3 *
4 * Copyright � 1993, 2000 Free Software Foundation, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this software; see the file COPYING. If not, write to
18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #ifdef WITH_DMALLOC
26 #include <dmalloc.h>
27 #endif
28
29 #include <ctype.h>
30 #include "sysdef.h"
31 #include "global.h"
32 #include "basic.h"
33 #include "cmd.h"
34 #include "regions.h"
35 #include "window.h"
36 #include "io-term.h"
37 #include "io-generic.h"
38 #include "io-abstract.h"
39 #include "io-utils.h"
40 #include "io-curses.h"
41 #include "ref.h"
42 #include "format.h"
43 #include "lists.h"
44 #include "io-edit.h"
45 #include "eval.h"
46 #include "byte-compile.h"
47 #include "decompile.h"
48 #include "userpref.h"
49
50
51 /* Used by motion commands. */
52 const int colmagic[] = {0, 0, 1, -1, 1, -1, 1, -1, 0};
53 const int rowmagic[] = {-1, 1, 0, 0, -1, -1, 1, 1, 0};
54
55 static char * motion_name[] =
56 {
57 "up",
58 "down",
59 "right",
60 "left",
61 "up right",
62 "up left",
63 "down right",
64 "down left",
65 "no motion"
66 };
67
68
69 /* This table ought to be a user parameter. */
70 static enum motion_magic complementary_motion[] =
71 {
72 magic_right,
73 magic_right,
74 magic_down,
75 magic_down,
76 magic_right,
77 magic_right,
78 magic_right,
79 magic_right,
80 magic_no_motion,
81 };
82
83 static enum motion_magic opposite_motion[] =
84 {
85 magic_down,
86 magic_up,
87 magic_left,
88 magic_right,
89 magic_down_left,
90 magic_down_right,
91 magic_up_left,
92 magic_up_right,
93 magic_no_motion,
94 };
95
96
97 /* Indexed by MAGIC + 1 */
98 const int boundrymagic[3] = { MIN_ROW, NON_ROW, MAX_ROW };
99
100
101 /* A very basic command. */
102
103 void
noop(void)104 noop (void)
105 {}
106
107
108 /* Commands that inser/delete rows/columns. */
109
110 void
insert_row(int repeat)111 insert_row (int repeat)
112 {
113 struct rng from;
114 struct rng to;
115 if ((repeat > (MAX_ROW - curow)) || (repeat < 0))
116 {
117 io_error_msg ("insert-row: prefix argument out of range.");
118 return;
119 }
120 from.lc = MIN_COL;
121 from.hc = MAX_COL;
122 from.lr = curow;
123 from.hr = MAX_ROW - repeat;
124 to.lc = MIN_COL;
125 to.hc = MIN_COL;
126 to.lr = curow + repeat;
127 to.hr = curow + repeat;
128 move_region (&from, &to);
129 }
130
131 void
insert_col(int repeat)132 insert_col (int repeat)
133 {
134 struct rng from;
135 struct rng to;
136 if ((repeat > (MAX_COL - cucol)) || (repeat < 0))
137 {
138 io_error_msg ("insert-col: prefix argument out of range.");
139 return;
140 }
141 from.lr = MIN_ROW;
142 from.hr = MAX_ROW;
143 from.lc = cucol;
144 from.hc = MAX_COL - repeat;
145 to.lr = MIN_ROW;
146 to.hr = MIN_ROW;
147 to.lc = cucol + repeat;
148 to.hc = cucol + repeat;
149 move_region (&from, &to);
150 }
151
152 void
delete_row(int repeat)153 delete_row (int repeat)
154 {
155 struct rng from;
156 struct rng to;
157 if ((repeat < 0) || (repeat > (MAX_ROW - curow + 1)))
158 {
159 io_error_msg ("delete-row: prefix argument out of range.");
160 return;
161 }
162 from.lc = MIN_COL;
163 from.hc = MAX_COL;
164 from.lr = curow + repeat;
165 from.hr = MAX_ROW;
166 to.lc = MIN_COL;
167 to.hc = MIN_COL;
168 to.lr = curow;
169 to.hr = curow;
170 move_region (&from, &to);
171 }
172
173 void
delete_col(int repeat)174 delete_col (int repeat)
175 {
176 struct rng from;
177 struct rng to;
178 if ((repeat < 0) || (repeat > (MAX_COL - cucol + 1)))
179 {
180 io_error_msg ("delete-col: prefix argument out of range.");
181 return;
182 }
183 from.lr = MIN_ROW;
184 from.hr = MAX_ROW;
185 from.lc = cucol + repeat;
186 from.hc = MAX_COL;
187 to.lr = MIN_ROW;
188 to.hr = MIN_ROW;
189 to.lc = cucol;
190 to.hc = cucol;
191 move_region (&from, &to);
192 }
193
194 /* Front end to the window functions. */
195
196 void
open_window(char * text)197 open_window (char *text)
198 {
199 int hv;
200 int where;
201
202 while (*text == ' ')
203 text++;
204
205 if (*text == 'h' || *text == 'H')
206 hv = 0;
207 else if (*text == 'v' || *text == 'V')
208 hv = 1;
209 else
210 {
211 io_error_msg ("Open 'h'orizontal or 'v'ertical window, not '%s'", text);
212 return;
213 }
214 where = atoi (text + 1);
215 while (isspace (*text))
216 ++text;
217 while (isalnum (*text))
218 ++text;
219 while (isspace (*text))
220 ++text;
221 if (*text == '%')
222 {
223 where *= (hv
224 ? (cwin->numr + (cwin->lh_wid ? label_rows : 0))
225 : (cwin->numc + cwin->lh_wid));
226 where /= 100;
227 }
228 io_win_open (hv, where);
229 }
230
231 void
hsplit_window(void)232 hsplit_window (void)
233 {
234 open_window ("h50%");
235 }
236
237
238 void
vsplit_window(void)239 vsplit_window (void)
240 {
241 open_window ("v50%");
242 }
243
244
245 void
close_window(char * text)246 close_window (char *text)
247 {
248 int num;
249
250 num = atoi (text) - 1;
251
252 if (num < 0 || num >= nwin)
253 {
254 io_error_msg ("Window %num?", text);
255 return;
256 }
257 if (nwin == 1)
258 {
259 io_error_msg ("You can't close the last window!");
260 return;
261 }
262 io_win_close (&wins[num]);
263 }
264
265 void
delete_window(void)266 delete_window (void)
267 {
268 io_win_close (cwin);
269 }
270
271 void
delete_other_windows(void)272 delete_other_windows (void)
273 {
274 if (nwin > 1)
275 {
276 CELLREF r = curow;
277 CELLREF c = cucol;
278 while (nwin > 1)
279 io_win_close (cwin);
280 io_move_cell_cursor (r, c);
281 }
282 }
283
284 void
nicely_goto_window(int n)285 nicely_goto_window (int n)
286 {
287 if (input_active)
288 {
289 io_cellize_cursor ();
290 window_after_input = n;
291 input_active = 0;
292 the_cmd_frame->top_keymap = map_id ("main");
293 return;
294 }
295 else
296 {
297 if ((window_after_input >= 0)
298 && ((window_after_input % nwin) == n))
299 {
300 io_inputize_cursor ();
301 window_after_input = -1;
302 input_active = 1;
303 the_cmd_frame->top_keymap =
304 map_id (the_cmd_frame->cmd
305 ? the_cmd_arg.style->keymap
306 : "main");
307 }
308 else
309 io_set_cwin (&wins[n]);
310 }
311 }
312
313 void
goto_minibuffer(void)314 goto_minibuffer (void)
315 {
316 if (window_after_input < 0)
317 {
318 if (!input_active)
319 io_error_msg ("Minibuffer not active.");
320 }
321 else
322 nicely_goto_window ((window_after_input % nwin));
323 }
324
325
326 void
goto_window(char * text)327 goto_window (char *text)
328 {
329 int n;
330 n = atoi (text) - 1;
331 if (n < 0 || n > nwin)
332 {
333 io_error_msg ("Window %s doesn't exist.", text);
334 return;
335 }
336 else
337 nicely_goto_window (n);
338 }
339
340
341 void
other_window(void)342 other_window (void)
343 {
344 int n = cwin - wins;
345 if (!input_active)
346 n = (n + 1) % nwin;
347 nicely_goto_window (n);
348 }
349
350 int
set_window_option(int set_opt,char * text)351 set_window_option (int set_opt, char *text)
352 {
353 int n;
354 int stat;
355 static struct opt
356 {
357 char *text;
358 int bits;
359 }
360 opts[] =
361 {
362 {
363 "reverse", WIN_EDGE_REV
364 }
365 ,
366 {
367 "standout", WIN_EDGE_REV
368 }
369 ,
370 {
371 "page", WIN_PAG_HZ | WIN_PAG_VT
372 }
373 ,
374 {
375 "pageh", WIN_PAG_HZ
376 }
377 ,
378 {
379 "pagev", WIN_PAG_VT
380 }
381 ,
382 {
383 "lockh", WIN_LCK_HZ
384 }
385 ,
386 {
387 "lockv", WIN_LCK_VT
388 }
389 ,
390 {
391 "edges", WIN_EDGES
392 }
393 };
394 if ((stat = (!strincmp (text, "status", 6) && isspace (text[6])))
395 || (!strincmp (text, "input", 5) && isspace (text[5])))
396 {
397 int n = set_opt ? atoi (text + 6 + stat) : 0; /* A little pun. */
398 int new_inp = stat ? user_input : n;
399 int new_stat = stat ? n : user_status;
400 io_set_input_status (new_inp, new_stat, 1);
401 }
402 else if (!strincmp (text, "link", 4))
403 {
404 if (set_opt)
405 {
406 n = atoi (text + 4) - 1;
407 if (n < 0 || n > nwin)
408 io_error_msg ("Can't '%s': window # out of range", text);
409 else
410 cwin->link = n;
411 }
412 else
413 cwin->link = -1;
414 }
415 else if (set_opt && !stricmp (text, "unlink"))
416 cwin->link = -1;
417 else if (set_opt && !strincmp (text, "row ", 4))
418 {
419 text += 4;
420 curow = astol (&text);
421 }
422 else if (set_opt && !strincmp (text, "col ", 4))
423 {
424 text += 4;
425 cucol = astol (&text);
426 }
427 else
428 {
429 for (n = 0; n < sizeof (opts) / sizeof (struct opt); n++)
430 if (!stricmp (text, opts[n].text))
431 {
432 if (set_opt)
433 cwin->flags |= opts[n].bits;
434 else
435 cwin->flags &= ~opts[n].bits;
436 break;
437 }
438
439 if (n == sizeof (opts) / sizeof (struct opt))
440 return 0;
441 }
442 return 1;
443 }
444
445 void
show_window_options(void)446 show_window_options (void)
447 {
448 int n;
449
450 cwin->win_curow = curow;
451 cwin->win_cucol = cucol;
452 if (user_status)
453 io_text_line ("Status line at %d", user_status);
454 else
455 io_text_line ("Status line disabled.");
456 io_text_line ("");
457 for (n = 0; n < nwin; n++)
458 {
459 int flags = wins[n].flags;
460 io_text_line ("Window #%d showing %s, with cursor at %s",
461 n + 1,
462 range_name (&wins[n].screen),
463 cell_name (wins[n].win_curow, wins[n].win_cucol));
464 io_text_line (" Options: %sedges (%sreverse)%s%s%s%s",
465 flags & WIN_EDGES ? "" : "no",
466 flags & WIN_EDGE_REV ? "" : "no",
467 flags & WIN_PAG_HZ ? ", pageh" : "",
468 flags & WIN_PAG_VT ? ", pagev" : "",
469 flags & WIN_LCK_HZ ? ", lockh" : "",
470 flags & WIN_LCK_VT ? ", lockv" : "");
471 if (wins[n].link != -1)
472 io_text_line ("Linked to window %d", wins[n].link + 1);
473 }
474 }
475
476 void
recenter_window(void)477 recenter_window (void)
478 {
479 io_recenter_cur_win ();
480 }
481
482 /* Trivial front-end commands. */
483
484
485 void
suspend_oleo(void)486 suspend_oleo (void)
487 {
488 if (using_curses)
489 {
490 stop_curses ();
491 #ifdef SIGTSTP
492 kill (getpid (), SIGTSTP);
493 #else
494 #ifdef SIGSTOP
495 kill (getpid (), SIGSTOP);
496 #endif
497 #endif
498 cont_curses ();
499 }
500 }
501
502 void
recalculate(int all)503 recalculate (int all)
504 {
505 current_cycle++;
506 if (all)
507 {
508 CELLREF row;
509 CELLREF col;
510 struct rng all;
511 all.lr = MIN_ROW;
512 all.hr = MAX_ROW;
513 all.lc = MIN_COL;
514 all.hc = MAX_COL;
515 find_cells_in_range (&all);
516 while (next_row_col_in_range (&row, &col)) {
517 #if 0
518 fprintf(stderr, "Recalculate cell %d.%d\n", row, col);
519 #endif
520 push_cell (row, col);
521 }
522 }
523 while (eval_next_cell ())
524 ;
525 }
526
527
528 void
kill_oleo(void)529 kill_oleo (void)
530 {
531 extern int option_filter;
532
533 if (option_filter) {
534 write_file_generic(stdout, NULL, NULL);
535 }
536
537 io_close_display(0);
538 exit (0);
539 }
540
541
542 void
kill_all_cmd(void)543 kill_all_cmd (void)
544 {
545 clear_spreadsheet ();
546 io_repaint ();
547 }
548
549 void
redraw_screen(void)550 redraw_screen (void)
551 {
552 io_repaint ();
553 }
554
555
556 /* Motion commands. */
557
558 void
shift_cell_cursor(int dir,int repeat)559 shift_cell_cursor (int dir, int repeat)
560 {
561 io_shift_cell_cursor (dir, repeat);
562 }
563
564
565 void
scroll_cell_cursor(int dir,int repeat)566 scroll_cell_cursor (int dir, int repeat)
567 {
568 io_scroll_cell_cursor (dir, repeat);
569 }
570
571
572
573 void
goto_region(struct rng * r)574 goto_region (struct rng *r)
575 {
576 (void) io_move_cell_cursor (r->lr, r->lc);
577
578 if (r->hr != r->lr || r->hc != r->lc) {
579 mkrow = r->hr;
580 mkcol = r->hc;
581 } else if (mkrow != NON_ROW) {
582 mkrow = NON_ROW;
583 }
584
585 io_update_status ();
586 }
587
588 void
goto_cell(struct rng * rng)589 goto_cell (struct rng * rng)
590 {
591 rng->hr = mkrow;
592 rng->hc = mkcol;
593 goto_region (rng);
594 }
595
596 void
exchange_point_and_mark(int clrmk)597 exchange_point_and_mark (int clrmk)
598 {
599 struct rng rng;
600 if (clrmk)
601 {
602 rng.lr = curow;
603 rng.lc = cucol;
604 rng.hr = NON_ROW;
605 rng.hc = NON_COL;
606 goto_region (&rng);
607 }
608 else if (mkrow != NON_ROW)
609 {
610 rng.lr = mkrow;
611 rng.lc = mkcol;
612 rng.hr = curow;
613 rng.hc = cucol;
614 goto_region (&rng);
615 }
616 }
617
618 static CELLREF
first_filled_col(CELLREF row)619 first_filled_col (CELLREF row)
620 {
621 struct rng rng;
622 CELLREF r;
623 CELLREF c;
624 rng.lr = row;
625 rng.hr = row;
626 rng.lc = MIN_COL;
627 rng.hc = MAX_COL;
628 find_cells_in_range (&rng);
629 while (1)
630 {
631 CELL * cp;
632 cp = next_row_col_in_range (&r, &c);
633 if (!cp)
634 break;
635 if (GET_TYP(cp))
636 {
637 no_more_cells ();
638 return c;
639 }
640 }
641 return NON_COL;
642 }
643
644 static CELLREF
last_filled_col(CELLREF row)645 last_filled_col (CELLREF row)
646 {
647 struct rng rng;
648 CELLREF r;
649 CELLREF c;
650 CELLREF bestc = MIN_COL;
651 rng.lr = row;
652 rng.hr = row;
653 rng.lc = MIN_COL;
654 rng.hc = MAX_COL;
655 find_cells_in_range (&rng);
656 while (1)
657 {
658 CELL * cp;
659 cp = next_row_col_in_range (&r, &c);
660 if (!cp)
661 break;
662 if (GET_TYP(cp))
663 bestc = c;
664 }
665 return bestc;
666 }
667
668 static CELLREF
first_filled_row(CELLREF col)669 first_filled_row (CELLREF col)
670 {
671 struct rng rng;
672 CELLREF r;
673 CELLREF c;
674 CELL * cp;
675 rng.lr = MIN_ROW;
676 rng.hr = MAX_ROW;
677 rng.lc = col;
678 rng.hc = col;
679 find_cells_in_range (&rng);
680 while (1)
681 {
682 cp = next_row_col_in_range (&r, &c);
683 if (!cp)
684 break;
685 if (GET_TYP(cp))
686 {
687 no_more_cells ();
688 return r;
689 }
690 }
691 return NON_ROW;
692 }
693
694 static CELLREF
last_filled_row(CELLREF col)695 last_filled_row (CELLREF col)
696 {
697 struct rng rng;
698 CELLREF r;
699 CELLREF c;
700 CELLREF bestr = MIN_ROW;
701 CELL * cp;
702 rng.lr = MIN_ROW;
703 rng.hr = MAX_ROW;
704 rng.lc = col;
705 rng.hc = col;
706 find_cells_in_range (&rng);
707 while (1)
708 {
709 cp = next_row_col_in_range (&r, &c);
710 if (!cp)
711 break;
712 if (GET_TYP(cp))
713 bestr = r;
714 }
715 return bestr;
716 }
717
718 static CELLREF
max_filled_row(void)719 max_filled_row (void)
720 {
721 CELLREF max_r = highest_row ();
722 while (max_r != MIN_ROW)
723 {
724 CELLREF c = first_filled_col (max_r);
725 if (c != NON_COL)
726 break;
727 --max_r;
728 }
729 return max_r;
730 }
731
732
733 static CELLREF
max_filled_col(void)734 max_filled_col (void)
735 {
736 CELLREF max_c = highest_col ();
737 while (max_c != MIN_COL)
738 {
739 CELLREF r = first_filled_row (max_c);
740 if (r != NON_COL)
741 break;
742 --max_c;
743 }
744 return max_c;
745 }
746
747 static void
mk_for_extreme(struct rng * rng)748 mk_for_extreme (struct rng * rng)
749 {
750 if (mkrow != NON_ROW)
751 {
752 rng->hr = mkrow;
753 rng->hc = mkcol;
754 }
755 else
756 {
757 rng->hc = cucol;
758 rng->hr = curow;
759 }
760 }
761
762 void
upper_left(void)763 upper_left (void)
764 {
765 struct rng rng;
766 rng.lr = MIN_ROW;
767 rng.lc = MIN_COL;
768 mk_for_extreme (&rng);
769 goto_region (&rng);
770 }
771
772 void
lower_left(void)773 lower_left (void)
774 {
775 struct rng rng;
776 rng.lr = max_filled_row ();
777 rng.lc = MIN_COL;
778 mk_for_extreme (&rng);
779 goto_region (&rng);
780 }
781
782 void
upper_right(void)783 upper_right (void)
784 {
785 struct rng rng;
786 rng.lr = MIN_ROW;
787 rng.lc = max_filled_col ();
788 mk_for_extreme (&rng);
789 goto_region (&rng);
790 }
791
792 void
lower_right(void)793 lower_right (void)
794 {
795 struct rng rng;
796 rng.lr = max_filled_row ();
797 rng.lc = max_filled_col ();
798 mk_for_extreme (&rng);
799 goto_region (&rng);
800 }
801
802 void
mark_cell_cmd(int popmk)803 mark_cell_cmd (int popmk)
804 {
805 if (popmk)
806 {
807 if (mkrow != NON_ROW)
808 {
809 struct rng rng;
810 rng.lr = mkrow;
811 rng.lc = mkcol;
812 rng.hr = NON_ROW;
813 rng.hc = NON_COL;
814 goto_region (&rng);
815 }
816 } else {
817 mkrow = curow;
818 mkcol = cucol;
819 io_update_status ();
820 }
821 }
822
823 void
unmark_cmd(void)824 unmark_cmd (void)
825 {
826 mkrow = NON_ROW;
827 mkcol = NON_COL;
828
829 io_update_status ();
830 }
831
832 void
save_mark_to_cell(struct rng * rng)833 save_mark_to_cell (struct rng * rng)
834 {
835 CELLREF row, col;
836 char * error;
837 struct rng r;
838
839 row = rng->lr;
840 col = rng->lc;
841
842 if (mkrow != NON_ROW )
843 set_rng (&r, mkrow, mkcol, mkrow, mkcol);
844 else
845 set_rng (&r, curow, cucol, curow, cucol);
846 error = new_value (row, col, backslash_a_string(range_name(&r), 1));
847 if (!error)
848 Global->modified = 1;
849 else
850 io_error_msg (error);
851 }
852
853 void
save_point_to_cell(struct rng * rng)854 save_point_to_cell (struct rng * rng)
855 {
856 CELLREF row, col;
857 char * error;
858 struct rng r;
859
860 row = rng->lr;
861 col = rng->lc;
862
863 set_rng (&r, curow, cucol, curow, cucol);
864
865 error = new_value (row, col, backslash_a_string(range_name(&r), 1));
866 if (!error)
867 Global->modified = 1;
868 else
869 io_error_msg (error);
870 }
871
872 /* This is a bit kludgey. Input line editting has its own event loop (grr!),
873 * and all of its state is private. These mouse commands can't entirely
874 * handle it when the target is in the input line. In that case, they
875 * save the decoded mouse event where io_get_line can pick it up:
876 */
877 struct mouse_event last_mouse_event;
878
879 void
do_mouse_goto(void)880 do_mouse_goto (void)
881 {
882 if (!last_mouse_event.downp)
883 return;
884 if (last_mouse_event.location >= 0 && last_mouse_event.downp)
885 {
886 if (input_active)
887 {
888 io_cellize_cursor ();
889 window_after_input = last_mouse_event.location;
890 input_active = 0;
891 the_cmd_frame->top_keymap = map_id ("main");
892 }
893 io_set_cwin (&wins[last_mouse_event.location]);
894 io_move_cell_cursor (last_mouse_event.r, last_mouse_event.c);
895 }
896 else if (last_mouse_event.location == MOUSE_ON_INPUT)
897 {
898 goto_minibuffer ();
899 #ifndef X_DISPLAY_MISSING
900 if (using_x)
901 goto_char (io_col_to_input_pos (last_mouse_event.col));
902 #endif
903 }
904 else
905 io_bell ();
906 }
907
908 void
do_mouse_mark(void)909 do_mouse_mark (void)
910 {
911 if (last_mouse_event.location >= 0 && last_mouse_event.downp) {
912 mkrow = last_mouse_event.r;
913 mkcol = last_mouse_event.c;
914 }
915 }
916
917
918 void
do_mouse_mark_and_goto(void)919 do_mouse_mark_and_goto (void)
920 {
921 if (last_mouse_event.location >= 0 && last_mouse_event.downp) {
922 mkrow = curow;
923 mkcol = cucol;
924 }
925 do_mouse_goto ();
926 }
927
928 void
do_mouse_cmd(void (* fn)())929 do_mouse_cmd (void (*fn) ())
930 {
931 int seq = real_get_chr ();
932 dequeue_mouse_event (&last_mouse_event, seq);
933 fn ();
934 }
935
936 void
mouse_mark_cmd(void)937 mouse_mark_cmd (void)
938 {
939 do_mouse_cmd (do_mouse_mark);
940 }
941
942
943 void
mouse_goto_cmd(void)944 mouse_goto_cmd (void)
945 {
946 do_mouse_cmd (do_mouse_goto);
947 }
948
949 void
mouse_mark_and_goto_cmd(void)950 mouse_mark_and_goto_cmd (void)
951 {
952 do_mouse_cmd (do_mouse_mark_and_goto);
953 }
954
955 /* Commands used to modify cell formulas. */
956
957 void
kill_cell_cmd(void)958 kill_cell_cmd (void)
959 {
960 CELL *cp;
961
962 cp = find_cell (curow, cucol);
963 if (!cp)
964 return;
965 if ((GET_LCK (cp) == LCK_DEF && default_lock == LCK_LCK) || GET_LCK (cp) == LCK_LCK)
966 {
967 io_error_msg ("Cell %s is locked", cell_name (curow, cucol));
968 return;
969 }
970 new_value (curow, cucol, "");
971 bzero(&(cp->cell_flags), sizeof(cp->cell_flags));
972 cp->cell_font = 0;
973 Global->modified = 1;
974 }
975
976
977 /* A front end to sorting. */
978
979 void
sort_region_cmd(char * ptr)980 sort_region_cmd (char *ptr)
981 {
982 struct rng tmp_rng;
983
984 if (get_abs_rng (&ptr, &sort_rng))
985 {
986 io_error_msg ("Can't find a range to sort in %s", ptr);
987 return;
988 }
989
990 cur_row = sort_rng.lr;
991 cur_col = sort_rng.lc;
992
993 while (*ptr == ' ')
994 ptr++;
995 if (!*ptr)
996 {
997 sort_ele.lr = 0;
998 sort_ele.lc = 0;
999 sort_ele.hr = 0;
1000 sort_ele.hc = 0;
1001 }
1002 else if (!parse_cell_or_range (&ptr, &sort_ele))
1003 {
1004 io_error_msg ("Can't parse elements in %s", ptr);
1005 return;
1006 }
1007 else
1008 {
1009 sort_ele.lr -= sort_rng.lr;
1010 sort_ele.lc -= sort_rng.lc;
1011 sort_ele.hr -= sort_rng.lr;
1012 sort_ele.hc -= sort_rng.lc;
1013 }
1014
1015 sort_keys_num = 0;
1016 while (*ptr == ' ')
1017 ptr++;
1018 for (; *ptr;)
1019 {
1020 if (sort_keys_num == sort_keys_alloc)
1021 {
1022 sort_keys_alloc++;
1023 if (sort_keys_alloc > 1)
1024 sort_keys = ck_realloc (sort_keys, sort_keys_alloc * sizeof (struct cmp));
1025 else
1026 sort_keys = ck_malloc (sizeof (struct cmp));
1027 }
1028 sort_keys[sort_keys_num].mult = 1;
1029 if (*ptr == '+')
1030 ptr++;
1031 else if (*ptr == '-')
1032 {
1033 sort_keys[sort_keys_num].mult = -1;
1034 ptr++;
1035 }
1036 if (!*ptr)
1037 {
1038 sort_keys[sort_keys_num].row = 0;
1039 sort_keys[sort_keys_num].col = 0;
1040 sort_keys_num++;
1041 break;
1042 }
1043 if (!parse_cell_or_range (&ptr, &tmp_rng) || tmp_rng.lr != tmp_rng.hr || tmp_rng.lc != tmp_rng.hc)
1044 {
1045 io_error_msg ("Can't parse key #%d in %s", sort_keys_num + 1, ptr);
1046 sort_keys_num = -1;
1047 return;
1048 }
1049 sort_keys[sort_keys_num].row = tmp_rng.lr - sort_rng.lr;
1050 sort_keys[sort_keys_num].col = tmp_rng.lc - sort_rng.lc;
1051 sort_keys_num++;
1052
1053 while (*ptr == ' ')
1054 ptr++;
1055 }
1056 if (sort_keys_num == 0)
1057 {
1058 if (sort_keys_alloc == 0)
1059 {
1060 sort_keys_alloc++;
1061 sort_keys = ck_malloc (sizeof (struct cmp));
1062 }
1063 sort_keys[0].mult = 1;
1064 sort_keys[0].row = 0;
1065 sort_keys[0].col = 0;
1066 sort_keys_num++;
1067 }
1068 sort_region ();
1069 io_repaint ();
1070 }
1071
1072
1073 void
imove(struct rng * rng,int ch)1074 imove (struct rng * rng, int ch)
1075 {
1076 if ((ch > 0) && (ch != 27))
1077 pushed_back_char = ch;
1078
1079 goto_region (rng);
1080 }
1081
1082 /* Incremental navigation
1083 *
1084 * This should be called in edit mode while gathering arguments
1085 * for a complex command. The expected the_cmd_arg.
1086 */
1087
1088 #define MIN(A,B) ((A) < (B) ? (A) : (B))
1089
1090 /* PAGE_RULE can be 0: page by rows, 1: cols, 2 shorter of rows/cols,
1091 * -1: don't page at all.
1092 */
1093
1094 void
inc_direction(int count,int page_rule,int hack_magic)1095 inc_direction (int count, int page_rule, int hack_magic)
1096 {
1097 if (check_editting_mode ())
1098 return;
1099
1100 if (page_rule >= 0)
1101 {
1102 int page_size;
1103
1104 switch (page_rule)
1105 {
1106 default:
1107 case 0:
1108 page_size = (cwin->screen.hr - cwin->screen.lr);
1109 break;
1110 case 1:
1111 page_size = (cwin->screen.hc - cwin->screen.lc);
1112 break;
1113 case 2:
1114 page_size = MIN ((cwin->screen.hr - cwin->screen.lr),
1115 (cwin->screen.hc - cwin->screen.lc));
1116 break;
1117 }
1118 count *= page_size;
1119 }
1120
1121 if (the_cmd_frame->cmd && the_cmd_arg.inc_cmd)
1122 the_cmd_arg.inc_cmd (hack_magic, count);
1123 }
1124
1125 /* The commands that move to the extreme of a row[col] may also move
1126 * forward or backward some number of col[row], according to the prefix
1127 * arg. This is the logic of that. This function returns the new col[row]
1128 * and operates on the presumption that MIN_ROW == MIN_COL and
1129 * MAX_ROW == MAX_COL.
1130 */
1131
1132 static CELLREF
extreme_cmd_orth_motion(int count,CELLREF current)1133 extreme_cmd_orth_motion (int count, CELLREF current)
1134 {
1135 --count;
1136 if (count > (MAX_ROW - current))
1137 count = (MAX_ROW - current);
1138 else if (-count > (current - MIN_ROW))
1139 count = (MIN_ROW - current);
1140 return current + count;
1141 }
1142
1143
1144 void
beginning_of_row(int count)1145 beginning_of_row (int count)
1146 {
1147 struct rng rng;
1148 rng.lr = extreme_cmd_orth_motion (count, curow);
1149 rng.lc = MIN_COL;
1150 rng.hr = mkrow;
1151 rng.hc = mkcol;
1152 goto_region (&rng);
1153 }
1154
1155 void
end_of_row(int count)1156 end_of_row (int count)
1157 {
1158 struct rng rng;
1159 rng.lr = extreme_cmd_orth_motion (count, curow);
1160 rng.lc = last_filled_col (rng.lr);
1161 rng.hr = mkrow;
1162 rng.hc = mkcol;
1163 goto_region (&rng);
1164 }
1165
1166 void
beginning_of_col(int count)1167 beginning_of_col (int count)
1168 {
1169 struct rng rng;
1170 rng.lr = MIN_ROW;
1171 rng.lc = extreme_cmd_orth_motion (count, cucol);
1172 rng.hr = mkrow;
1173 rng.hc = mkcol;
1174 goto_region (&rng);
1175 }
1176
1177 void
end_of_col(int count)1178 end_of_col (int count)
1179 {
1180 struct rng rng;
1181 rng.lc = extreme_cmd_orth_motion (count, cucol);
1182 rng.lr = last_filled_row (rng.lc);
1183 rng.hr = mkrow;
1184 rng.hc = mkcol;
1185 goto_region (&rng);
1186 }
1187
1188
1189 static void
skip_empties(CELLREF * rout,CELLREF * cout,int magic)1190 skip_empties (CELLREF * rout, CELLREF * cout, int magic)
1191 {
1192 CELLREF r = *rout;
1193 CELLREF c = *cout;
1194 CELL * cp = find_cell (r, c);
1195
1196 while (!cp || !GET_TYP (cp))
1197 {
1198 if (r != boundrymagic [rowmagic [magic] + 1])
1199 r += rowmagic [magic];
1200 else if (rowmagic [magic])
1201 break;
1202 if (c != boundrymagic [colmagic [magic] + 1])
1203 c += colmagic [magic];
1204 else if (colmagic[magic])
1205 break;
1206 cp = find_cell (r, c);
1207 }
1208
1209 *rout = r;
1210 *cout = c;
1211 }
1212
1213 void
scan_cell_cursor(int magic,int count)1214 scan_cell_cursor (int magic, int count)
1215 {
1216 CELLREF r = curow;
1217 CELLREF c = cucol;
1218 CELLREF last_r = r;
1219 CELLREF last_c = c;
1220
1221 skip_empties (&r, &c, magic);
1222 {
1223 CELL * cp = find_cell (r, c);
1224 if (!(cp && GET_TYP (cp)))
1225 return;
1226 }
1227 while (count)
1228 {
1229 CELL * cp = find_cell (r, c);
1230 while (!cp || !GET_TYP (cp))
1231 {
1232 if (r != boundrymagic [rowmagic [magic] + 1])
1233 r += rowmagic [magic];
1234 else if (rowmagic [magic])
1235 break;
1236 if (c != boundrymagic [colmagic [magic] + 1])
1237 c += colmagic [magic];
1238 else if (colmagic[magic])
1239 break;
1240 cp = find_cell (r, c);
1241 }
1242 while (cp && GET_TYP (cp))
1243 {
1244 if (r != boundrymagic [rowmagic [magic] + 1])
1245 r += rowmagic [magic];
1246 else if (rowmagic[magic])
1247 break;
1248 if (c != boundrymagic [colmagic [magic] + 1])
1249 c += colmagic [magic];
1250 else if (colmagic [magic])
1251 break;
1252 last_r = r;
1253 last_c = c;
1254 cp = find_cell (r, c);
1255 }
1256 --count;
1257 }
1258 {
1259 struct rng rng;
1260 rng.lr = last_r;
1261 rng.lc = last_c;
1262 rng.hr = mkrow;
1263 rng.hc = mkcol;
1264 goto_region (&rng);
1265 }
1266 }
1267
1268
1269 void
edit_cell(char * new_formula)1270 edit_cell (char * new_formula)
1271 {
1272 char * fail;
1273 fail = new_value (setrow, setcol, new_formula);
1274 if (fail)
1275 io_error_msg (fail);
1276 else
1277 Global->modified = 1;
1278 }
1279
1280
1281 void
set_region_formula(struct rng * rng,char * str)1282 set_region_formula (struct rng * rng, char * str)
1283 {
1284 CELLREF row, col;
1285
1286 for (row = rng->lr; row <= rng->hr; ++row)
1287 for (col = rng->lc; col <= rng->hc; ++col)
1288 {
1289 char * error = new_value (row, col, str);
1290 if (!error)
1291 Global->modified = 1;
1292 if (error)
1293 {
1294 io_error_msg (error);
1295 return;
1296 }
1297 }
1298 }
1299
1300 void
goto_edit_cell(int c)1301 goto_edit_cell (int c)
1302 {
1303 pushed_back_char = c;
1304 execute_command ("edit-cell");
1305 }
1306
1307 /* This allows keys to be bound for immediate destructive editing.
1308 */
1309
1310 void
goto_set_cell(int c)1311 goto_set_cell (int c)
1312 {
1313 pushed_back_char = c;
1314 execute_command ("set-cell");
1315 }
1316
1317 void
read_cmds_cmd(FILE * fp)1318 read_cmds_cmd (FILE *fp)
1319 {
1320 struct line line;
1321 char *ptr;
1322 init_line (&line);
1323 Global->sneaky_linec = 0;
1324 while (read_line (&line, fp, &Global->sneaky_linec)) {
1325 #if 0
1326 fprintf(stderr, "RunCommand(%s)\n", line.buf);
1327 #endif
1328 for (ptr = line.buf; isspace (*ptr); ptr++);
1329 if (!*ptr || (*ptr == '#'))
1330 continue;
1331 execute_command (ptr);
1332 }
1333 }
1334
1335
1336 static char load_hooks_string[] = "load_hooks";
1337
1338 /*
1339 * Extended this to detect the extension of a file and have the right
1340 * read function process this.
1341 */
1342 void
read_file_and_run_hooks(FILE * fp,int ismerge,char * name)1343 read_file_and_run_hooks (FILE * fp, int ismerge, char * name)
1344 {
1345 char *ext = NULL;
1346 if (!ismerge)
1347 {
1348 FileSetCurrentFileName(name ? ck_savestr (name) : 0);
1349 }
1350 ext = strrchr(name, '.');
1351 if (! ext) {
1352 read_file_generic(fp, ismerge, NULL, name);
1353 } else {
1354 ext++;
1355 read_file_generic(fp, ismerge, ext, name);
1356 }
1357
1358 if (UserPreferences.run_load_hooks)
1359 {
1360 struct var * v;
1361 v = find_var (load_hooks_string, sizeof(load_hooks_string)-1);
1362 if (v && v->var_flags != VAR_UNDEF)
1363 execute_command (load_hooks_string);
1364 }
1365 }
1366
1367 /* If TURN_ON is 0, this toggles whether load hooks are run.
1368 * Otherwise, it turns load hooks on.
1369 */
1370
1371 void
toggle_load_hooks(int turn_on)1372 toggle_load_hooks (int turn_on)
1373 {
1374 if (!turn_on && UserPreferences.run_load_hooks)
1375 {
1376 UserPreferences.run_load_hooks = 0;
1377 io_info_msg ("load hooks turned off");
1378 }
1379 else
1380 {
1381 UserPreferences.run_load_hooks = 1;
1382 io_info_msg ("load hooks turned on");
1383 }
1384 }
1385
1386 void
write_cmd(FILE * fp,char * name)1387 write_cmd (FILE *fp, char * name)
1388 {
1389 FileSetCurrentFileName(name ? ck_savestr (name) : 0);
1390 (*write_file) (fp, 0);
1391 Global->modified = 0;
1392 }
1393
1394 void
read_cmd(FILE * fp,char * name)1395 read_cmd (FILE *fp, char * name)
1396 {
1397 read_file_and_run_hooks (fp, 0, name);
1398 }
1399
1400 void
read_merge_cmd(FILE * fp)1401 read_merge_cmd (FILE *fp)
1402 {
1403 (*read_file) (fp, 1);
1404 }
1405
1406 void
write_reg_cmd(FILE * fp,struct rng * rng)1407 write_reg_cmd (FILE *fp, struct rng *rng)
1408 {
1409 (*write_file) (fp, rng);
1410 }
1411
1412
1413 /* Cell attributes. */
1414 void
set_region_height(struct rng * rng,char * height)1415 set_region_height (struct rng * rng, char * height)
1416 {
1417 int hgt;
1418 char * saved_height = height;
1419
1420 while (isspace (*height))
1421 ++height;
1422
1423 if ( !*height
1424 || words_imatch (&height, "d")
1425 || words_imatch (&height, "def")
1426 || words_imatch (&height, "default"))
1427 hgt = 0;
1428 else
1429 {
1430 hgt = astol (&height) + 1;
1431 if (hgt < 0)
1432 /* noreturn */
1433 io_error_msg ("Height (%d) can't be less than 0.", hgt);
1434 }
1435
1436 if (*height)
1437 {
1438 io_error_msg ("Unknown height '%s'", saved_height);
1439 /* Doesn't return */
1440 }
1441 {
1442 CELLREF cc;
1443 for (cc = rng->lr; ;cc++)
1444 {
1445 set_height (cc, hgt);
1446 if (cc == rng->hr) /* This test goes here to prevent overflow. */
1447 break;
1448 }
1449 io_recenter_all_win ();
1450 }
1451 }
1452
1453 void
set_region_width(struct rng * rng,char * width)1454 set_region_width (struct rng * rng, char * width)
1455 {
1456 char * saved_width = width;
1457 int wid;
1458
1459 while (isspace (*width))
1460 ++width;
1461
1462 if ( !*width
1463 || words_imatch (&width, "d")
1464 || words_imatch (&width, "def")
1465 || words_imatch (&width, "default"))
1466 wid = 0;
1467 else
1468 {
1469 wid = astol (&width) + 1;
1470 if (wid < 0)
1471 /* noreturn */
1472 io_error_msg ("Width (%d) can't be less than 0.", wid);
1473 }
1474
1475 if (*width)
1476 {
1477 io_error_msg ("Unknown width '%s'", saved_width);
1478 /* No return. */
1479 }
1480 {
1481 CELLREF cc;
1482 for (cc = rng->lc; ;cc++)
1483 {
1484 set_width (cc, wid);
1485 if (cc == rng->hc) /* This test goes here to prevent overflow. */
1486 break;
1487 }
1488 io_recenter_all_win ();
1489 }
1490 }
1491
1492
1493 /* PROT may be `d', `p', or `u'. */
1494
1495 void
set_region_protection(struct rng * rng,int prot)1496 set_region_protection (struct rng * rng, int prot)
1497 {
1498 if (isupper (prot))
1499 prot = tolower (prot);
1500 switch (prot)
1501 {
1502 case 'd':
1503 lock_region (rng, LCK_DEF);
1504 break;
1505 case 'p':
1506 lock_region (rng, LCK_LCK);
1507 break;
1508 case 'u':
1509 lock_region (rng, LCK_UNL);
1510 break;
1511 default:
1512 io_error_msg ("Bad argument to protect-region %c.", prot);
1513 break;
1514 }
1515 }
1516
1517 void
set_region_alignment(struct rng * rng,int align)1518 set_region_alignment (struct rng * rng, int align)
1519 {
1520 int fun = chr_to_jst (align);
1521 if (fun != -1)
1522 format_region (rng, -1, fun);
1523 else /* if (main_map[align]!=BREAK_CMD) */
1524 io_error_msg ("Unknown Justify '%s'", char_to_string (align));
1525 }
1526
1527 /*
1528 * Lacking more knowledge of Oleo internals, we're hacking this thing to
1529 * clean up the mess concerning the mixup of format and precision.
1530 *
1531 * The function set_region_format is a pass-through between the command
1532 * loop and format_region(). As I don't know how to pass more than
1533 * one parameter from str_to_fmt over the command loop into
1534 * set_region_format, I'm leaving the mixup mentioned above as it
1535 * is in that area. But *only* for passing the information to here.
1536 * Here the first thing we do is take the two values apart and call the
1537 * clean API's with the right values.
1538 *
1539 * Sigh.
1540 * FIX ME
1541 *
1542 * Another place turned up that needs this : set_def_format().
1543 */
1544 void
set_region_format(struct rng * rng,int fmt)1545 set_region_format (struct rng * rng, int fmt)
1546 {
1547 int format = (fmt & FMT_MASK) >> FMT_SHIFT;
1548 int precision = fmt & PREC_MASK;
1549
1550 format_region (rng, format, -1);
1551 precision_region(rng, precision);
1552 }
1553
1554
1555 void
set_def_height(char * height)1556 set_def_height (char * height)
1557 {
1558 char * saved_height = height;
1559 int hgt;
1560
1561 while (isspace (*height))
1562 ++height;
1563
1564 if ( !*height
1565 || words_imatch (&height, "d")
1566 || words_imatch (&height, "def")
1567 || words_imatch (&height, "default"))
1568 hgt = 1;
1569 else
1570 {
1571 hgt = astol (&height);
1572 if (hgt < 0)
1573 /* noreturn */
1574 io_error_msg ("Height (%d) can't be less than 0.", hgt);
1575 }
1576
1577 if (*height)
1578 {
1579 io_error_msg ("Unknown height '%s'", saved_height);
1580 /* No return. */
1581 }
1582 default_height = hgt;
1583 io_recenter_all_win ();
1584 }
1585
1586 void
set_def_width(char * width)1587 set_def_width (char * width)
1588 {
1589 char * saved_width = width;
1590 int wid;
1591
1592 while (isspace (*width))
1593 ++width;
1594
1595 if ( !*width
1596 || words_imatch (&width, "d")
1597 || words_imatch (&width, "def")
1598 || words_imatch (&width, "default"))
1599 wid = 8;
1600 else
1601 {
1602 wid = astol (&width);
1603 if (wid < 0)
1604 /* noreturn */
1605 io_error_msg ("Width (%d) can't be less than 0.", wid);
1606 }
1607
1608 if (*width)
1609 {
1610 io_error_msg ("Unknown width '%s'", saved_width);
1611 /* No return. */
1612 }
1613 default_width = wid;
1614 io_recenter_all_win ();
1615 }
1616
1617 /* PROT may be `d', `p', or `u'. */
1618
1619 void
set_def_protection(int prot)1620 set_def_protection (int prot)
1621 {
1622 if (isupper (prot))
1623 prot = tolower (prot);
1624 switch (prot)
1625 {
1626 case 'p':
1627 default_lock = LCK_LCK;
1628 break;
1629 case 'u':
1630 default_lock = LCK_UNL;
1631 break;
1632 default:
1633 io_error_msg ("Bad argument to set-default-protection %c.", prot);
1634 break;
1635 }
1636 }
1637
1638 void
set_def_alignment(int align)1639 set_def_alignment (int align)
1640 {
1641 int fun = chr_to_jst (align);
1642 if (fun == -1)
1643 io_error_msg ("Unknown justification.");
1644
1645 default_jst = fun;
1646 io_repaint ();
1647 }
1648
1649 void
set_def_format(int fmt)1650 set_def_format (int fmt)
1651 {
1652 int format = (fmt & FMT_MASK) >> FMT_SHIFT;
1653 int precision = fmt & PREC_MASK;
1654
1655 default_fmt = format;
1656 default_prc = precision;
1657
1658 io_repaint ();
1659 }
1660
1661 void
define_usr_fmt(int fmt,char * pos_h,char * neg_h,char * pos_t,char * neg_t,char * zero,char * comma,char * decimal,char * precision,char * scale)1662 define_usr_fmt (int fmt, char * pos_h, char * neg_h, char * pos_t,
1663 char * neg_t, char * zero, char * comma, char * decimal,
1664 char * precision, char * scale)
1665 {
1666 char * usr_buf[9];
1667 if (fmt < 1 || fmt > 16)
1668 {
1669 io_error_msg ("Format number out of range %d (should be in [1..16].",
1670 fmt);
1671 /* no return */
1672 }
1673 /* Vector to an older interface... */
1674 --fmt;
1675 usr_buf[0] = pos_h;
1676 usr_buf[1] = neg_h;
1677 usr_buf[2] = pos_t;
1678 usr_buf[3] = neg_t;
1679 usr_buf[4] = zero;
1680 usr_buf[5] = comma;
1681 usr_buf[6] = decimal;
1682 usr_buf[7] = precision;
1683 usr_buf[8] = scale;
1684 set_usr_stats (fmt, usr_buf);
1685 }
1686
1687 /* Automatic motion while editting cell's: */
1688
1689 /* Hmm... where should this variable *really* go? */
1690
1691 void
set_auto_direction(enum motion_magic magic)1692 set_auto_direction (enum motion_magic magic)
1693 {
1694 Global->auto_motion_direction = magic;
1695 io_info_msg ("Auto-motion direction = %s.", motion_name[magic]);
1696 }
1697
1698 void
auto_move(void)1699 auto_move (void)
1700 {
1701 shift_cell_cursor (Global->auto_motion_direction, 1);
1702 }
1703
1704 void
auto_next_set(void)1705 auto_next_set (void)
1706 {
1707 scan_cell_cursor (opposite_motion[Global->auto_motion_direction], 1);
1708 {
1709 CELL * cp = find_cell (curow, cucol);
1710 if (!cp || !GET_TYP(cp))
1711 shift_cell_cursor (Global->auto_motion_direction, 1);
1712 }
1713 shift_cell_cursor (complementary_motion[Global->auto_motion_direction], 1);
1714 }
1715
1716 /* This decompiles and then recompiles all of the formulas of cells.
1717 * This is never normally necessary unless you happen to have some
1718 * spreadsheets written when the byte-code compiler had bugs that
1719 * made your formulas produce parse errors.
1720 */
1721
1722 void
recompile_spreadsheet(void)1723 recompile_spreadsheet (void)
1724 {
1725 struct rng rng;
1726 CELL * cp;
1727 CELLREF r;
1728 CELLREF c;
1729 rng.lr = MIN_ROW;
1730 rng.lc = MIN_COL;
1731 rng.hr = MAX_ROW;
1732 rng.hc = MAX_COL;
1733 find_cells_in_range (&rng);
1734 for (cp = next_row_col_in_range (&r, &c);
1735 cp;
1736 cp = next_row_col_in_range(&r, &c))
1737 {
1738 char * form = decomp (r, c, cp);
1739 set_cell (r, c, form);
1740 if (my_cell)
1741 {
1742 update_cell (my_cell);
1743 if (is_constant (my_cell->cell_formula))
1744 {
1745 byte_free (my_cell->cell_formula);
1746 my_cell->cell_formula = 0;
1747 }
1748 io_pr_cell (r, c, my_cell);
1749 my_cell = 0;
1750 }
1751 decomp_free ();
1752 }
1753 }
1754
1755