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