1 /* SC A Spreadsheet Calculator
2 *
3 * One line vi emulation
4 * $Revision: 1.1 $
5 */
6
7 #include <config.h>
8 #include <curses.h>
9 #include <sys/types.h>
10
11 #include <signal.h>
12 #ifdef HAVE_X11_X_H
13 #include <X11/Xlib.h>
14 #include <X11/Xutil.h>
15 #endif /* HAVE_X11_X_H */
16
17 #include <ctype.h>
18 #include "sc.h"
19
20 #ifdef HAVE_X11_X_H
21 #include "scXstuff.h"
22 #endif
23
24 #define istext(a) (isalnum(a) || ((a) == '_'))
25
26 static void append_line PROTO((void));
27 static void back_hist PROTO((void));
28 static int back_line PROTO((void));
29 static int back_word PROTO((void));
30 static void back_space PROTO((void));
31 static void change_cmd PROTO((void));
32 static void col_0 PROTO((void));
33 static void delete_cmd PROTO((void));
34 static void del_chars PROTO((int, int));
35 static void del_in_line PROTO((void));
36 static void del_to_end PROTO((void));
37 static void dotcmd PROTO((void));
38 static int end_line PROTO((void));
39 static int find_char PROTO((void));
40 static void for_hist PROTO((void));
41 static int for_line PROTO((int));
42 static int for_word PROTO((int));
43 static int get_motion PROTO((void));
44 static void ins_in_line PROTO((int));
45 static void last_col PROTO((void));
46 static void rep_char PROTO((void));
47 static void replace_in_line PROTO((int));
48 static void replace_mode PROTO((void));
49 static void restore_it PROTO((void));
50 static void savedot PROTO((int));
51 static void save_hist PROTO((void));
52 static void search_again PROTO((void));
53 static void search_hist PROTO((void));
54 static void search_mode PROTO((void));
55 static void stop_edit PROTO((void));
56 static int to_char PROTO((void));
57 static void u_save PROTO((int));
58
59 extern int showrange;
60 extern char mode_ind; /* Mode indicator */
61
62 /* values for mode below */
63
64 #define INSERT_MODE 0 /* Insert mode */
65 #define EDIT_MODE 1 /* Edit mode */
66 #define REP_MODE 2 /* Replace mode */
67 #define SEARCH_MODE 3 /* Get arguments for '/' command */
68
69 #define DOTLEN 200
70
71 static int mode = INSERT_MODE;
72 struct hist {
73 unsigned int len;
74 char *histline;
75 } history[HISTLEN];
76
77 static int histp = -1;
78 static int lasthist = -1;
79 static int endhist = -1;
80 static char *last_search = NULL;
81 static char *undo_line = NULL;
82 static int undo_lim;
83 static char dotb[DOTLEN];
84 static int doti = 0;
85 static int do_dot = 0;
86
87 void
write_line(c)88 write_line(c)
89 int c;
90 {
91 if (mode == EDIT_MODE) {
92 switch(c) {
93 case (ctl('h')): linelim = back_line(); break;
94 case (ctl('m')): cr_line(); break;
95 case ESC: stop_edit(); break;
96 case '+': for_hist(); break;
97 case '-': back_hist(); break;
98 case '$': last_col(); break;
99 case '.': dotcmd(); break;
100 case '/': search_mode(); break;
101 case '0': col_0(); break;
102 case 'C': u_save(c);del_to_end(); last_col();
103 append_line(); break;
104 case 'D': u_save(c);del_to_end(); break;
105 case 'I': u_save(c);col_0();insert_mode();break;
106 case 'R': replace_mode(); break;
107 case 'X': u_save(c); back_space(); break;
108 case 'a': u_save(c); append_line(); break;
109 case 'A': u_save(c);last_col();append_line(); break;
110 case 'b': linelim = back_word(); break;
111 case 'c': u_save(c); change_cmd(); break;
112 case 'd': u_save(c); delete_cmd(); break;
113 case 'f': linelim = find_char(); break;
114 case 'h': linelim = back_line(); break;
115 case 'i': u_save(c); insert_mode(); break;
116 case 'j': for_hist(); break;
117 case 'k': back_hist(); break;
118 case ' ':
119 case 'l': linelim = for_line(0); break;
120 case 'n': search_again(); break;
121 case 'q': stop_edit(); break;
122 case 'r': u_save(c); rep_char(); break;
123 case 't': linelim = to_char(); break;
124 case 'u': restore_it(); break;
125 case 'w': linelim = for_word(0); break;
126 case 'x': u_save(c); del_in_line(); break;
127 default: break;
128 }
129 } else if (mode == INSERT_MODE) {
130 savedot(c);
131 switch(c) {
132 case (ctl('h')): back_space(); break;
133 case (ctl('m')): cr_line(); break;
134 case ESC: edit_mode(); break;
135 default: ins_in_line(c); break;
136 }
137 } else if (mode == SEARCH_MODE) {
138 switch(c) {
139 case (ctl('h')): back_space(); break;
140 case (ctl('m')): search_hist(); break;
141 case ESC: edit_mode(); break;
142 default: ins_in_line(c); break;
143 }
144 } else if (mode == REP_MODE) {
145 savedot(c);
146 switch(c) {
147 case (ctl('h')): back_space(); break;
148 case (ctl('m')): cr_line(); break;
149 case ESC: edit_mode(); break;
150 default: replace_in_line(c); break;
151 }
152 }
153 }
154
155 void
edit_mode()156 edit_mode()
157 {
158 mode = EDIT_MODE;
159 mode_ind = 'e';
160 histp = -1;
161 if (linelim < 0) /* -1 says stop editing, ...so we still aren't */
162 return;
163 if (line[linelim] == '\0')
164 linelim = back_line();
165 }
166
167 void
insert_mode()168 insert_mode()
169 {
170 mode_ind = 'i';
171 mode = INSERT_MODE;
172 }
173
174 static void
search_mode()175 search_mode()
176 {
177 line[0] = '/';
178 line[1] = '\0';
179 linelim = 1;
180 histp = -1;
181 mode_ind = '/';
182 mode = SEARCH_MODE;
183 }
184
185 static void
replace_mode()186 replace_mode()
187 {
188 mode_ind = 'R';
189 mode = REP_MODE;
190 }
191
192 /* dot command functions. Saves info so we can redo on a '.' command */
193
194 static void
savedot(c)195 savedot(c)
196 int c;
197 {
198 if (do_dot || (c == '\n'))
199 return;
200
201 if (doti < DOTLEN-1)
202 {
203 dotb[doti++] = c;
204 dotb[doti] = '\0';
205 }
206 }
207
208 static int dotcalled = 0;
209
210 static void
dotcmd()211 dotcmd()
212 {
213 int c;
214
215 if (dotcalled) /* stop recursive calling of dotcmd() */
216 return;
217 do_dot = 1;
218 doti = 0;
219 while(dotb[doti] != '\0') {
220 c = dotb[doti++];
221 dotcalled = 1;
222 write_line(c);
223 }
224 do_dot = 0;
225 doti = 0;
226 dotcalled = 0;
227 }
228
229 int
vigetch()230 vigetch()
231 {
232 int c;
233
234 if(do_dot) {
235 if (dotb[doti] != '\0') {
236 return(dotb[doti++]);
237 } else {
238 do_dot = 0;
239 doti = 0;
240 return(nmgetch());
241 }
242 }
243 c = nmgetch();
244 savedot(c);
245 return(c);
246 }
247
248 /* saves the current line for possible use by an undo cmd */
249 static void
u_save(c)250 u_save(c)
251 int c;
252 { static unsigned undolen = 0;
253
254 if (strlen(line)+1 > undolen)
255 { undolen = strlen(line)+40;
256
257 undo_line = scxrealloc(undo_line, undolen);
258 }
259 (void) strcpy(undo_line, line);
260
261 undo_lim = linelim;
262
263 /* reset dot command if not processing it. */
264
265 if (!do_dot) {
266 doti = 0;
267 savedot(c);
268 }
269 }
270
271 /* Restores the current line saved by u_save() */
272 static void
restore_it()273 restore_it()
274 {
275 static char *tempc = NULL;
276 static unsigned templen = 0;
277 int tempi;
278
279 if ((undo_line == NULL) || (*undo_line == '\0'))
280 return;
281
282 if (strlen(line)+1 > templen)
283 { templen = strlen(line)+40;
284 tempc = scxrealloc(tempc, templen);
285 }
286
287 strcpy(tempc, line);
288 tempi = linelim;
289 (void) strcpy(line, undo_line);
290 linelim = undo_lim;
291 strcpy(undo_line, tempc);
292 undo_lim = tempi;
293 }
294
295 /* This command stops the editing process. */
296 static void
stop_edit()297 stop_edit()
298 {
299 showrange = 0;
300 linelim = -1;
301 if (!using_X)
302 { (void) move(1, 0);
303 (void) clrtoeol();
304 }
305 }
306
307 /*
308 * Motion commands. Forward motion commands take an argument
309 * which, when set, cause the forward motion to continue onto
310 * the null at the end of the line instead of stopping at the
311 * the last character of the line.
312 */
313 static int
for_line(stop_null)314 for_line(stop_null)
315 int stop_null;
316 {
317 if (linelim >= 0 && line[linelim] != '\0' &&
318 (line[linelim+1] != '\0' || stop_null))
319 return(linelim+1);
320 else
321 return(linelim);
322 }
323
324 static int
end_line()325 end_line()
326 { return (strlen(line));
327 }
328
329
330 static int
for_word(stop_null)331 for_word(stop_null)
332 int stop_null;
333 {
334 register int c;
335 register int cpos;
336
337 cpos = linelim;
338
339 if (line[cpos] == ' ') {
340 while (line[cpos] == ' ')
341 cpos++;
342 if (cpos > 0 && line[cpos] == '\0')
343 --cpos;
344 return(cpos);
345 }
346
347 if (istext(line[cpos])) {
348 while ((c = line[cpos]) && istext(c))
349 cpos++;
350 } else {
351 while ((c = line[cpos]) && !istext(c) && c != ' ')
352 cpos++;
353 }
354
355 while (line[cpos] == ' ')
356 cpos++;
357
358 if (cpos > 0 && line[cpos] == '\0' && !stop_null)
359 --cpos;
360
361 return(cpos);
362 }
363
364 static int
back_line()365 back_line()
366 {
367 if (linelim)
368 return(linelim-1);
369 else
370 return(0);
371 }
372
373 static int
back_word()374 back_word()
375 {
376 register int c;
377 register int cpos;
378
379 cpos = linelim;
380
381 if (line[cpos] == ' ') {
382 /* Skip white space */
383 while (cpos > 0 && line[cpos] == ' ')
384 --cpos;
385 } else if (cpos > 0 && (line[cpos-1] == ' '
386 || (istext(line[cpos]) && !istext(line[cpos-1]))
387 || (!istext(line[cpos]) && istext(line[cpos-1])))) {
388 /* Started on the first char of a word - back up to prev. word */
389 --cpos;
390 while (cpos > 0 && line[cpos] == ' ')
391 --cpos;
392 }
393
394 /* Skip across the word - goes 1 too far */
395 if (istext(line[cpos])) {
396 while (cpos > 0 && (c = line[cpos]) && istext(c))
397 --cpos;
398 } else {
399 while (cpos > 0 && (c = line[cpos]) && !istext(c) && c != ' ')
400 --cpos;
401 }
402
403 /* We are done - fix up the one too far */
404 if (cpos > 0 && line[cpos] && line[cpos+1])
405 cpos++;
406
407 return(cpos);
408 }
409
410 /* Text manipulation commands */
411
412 static void
del_in_line()413 del_in_line()
414 {
415 register int len, i;
416
417 if (linelim >= 0) {
418 len = strlen(line);
419 if (linelim == len && linelim > 0)
420 linelim--;
421 for (i = linelim; i < len; i++)
422 line[i] = line[i+1];
423 }
424 if (linelim > 0 && line[linelim] == '\0')
425 --linelim;
426 }
427
428 static void
ins_in_line(c)429 ins_in_line(c)
430 int c;
431 {
432 register int i, len;
433
434 if (linelim < 0)
435 { *line = '\0';
436 linelim = 0;
437 }
438 len = strlen(line);
439 for (i = len; i >= linelim; --i)
440 line[i+1] = line[i];
441 line[linelim++] = c;
442 line[len+1] = '\0';
443 }
444
445 void
ins_string(s)446 ins_string(s)
447 char *s;
448 {
449 while (*s)
450 ins_in_line(*s++);
451 }
452
453 static void
append_line()454 append_line()
455 {
456 register int i;
457
458 i = linelim;
459 if (i >= 0 && line[i])
460 linelim++;
461 insert_mode();
462 }
463
464 static void
rep_char()465 rep_char()
466 {
467 int c;
468
469 if (linelim < 0)
470 { linelim = 0;
471 *line = '\0';
472 }
473 c = vigetch();
474 if (line[linelim] != '\0') {
475 line[linelim] = c;
476 } else {
477 line[linelim] = c;
478 line[linelim+1] = '\0';
479 }
480 }
481
482 static void
replace_in_line(c)483 replace_in_line(c)
484 int c;
485 {
486 register int len;
487
488 if (linelim < 0)
489 { linelim = 0;
490 *line = '\0';
491 }
492 len = strlen(line);
493 line[linelim++] = c;
494 if (linelim > len)
495 line[linelim] = '\0';
496 }
497
498 static void
back_space()499 back_space()
500 {
501 if (linelim == 0)
502 return;
503
504 if (line[linelim] == '\0') {
505 linelim = back_line();
506 del_in_line();
507 linelim = strlen(line);
508 } else {
509 linelim = back_line();
510 del_in_line();
511 }
512 }
513
514 static int
get_motion()515 get_motion()
516 {
517 int c;
518
519 c = vigetch();
520 switch (c) {
521 case 'b': return(back_word());
522 case 'f': return(find_char()+1);
523 case 'h': return(back_line());
524 case 'l': return(for_line(1));
525 case 't': return(to_char()+1);
526 case 'w': return(for_word(1));
527 case '0': return (0);
528 case '$': return(end_line());
529 default: return(linelim);
530 }
531 }
532
533 static del_end;
534
535 static void
delete_cmd()536 delete_cmd()
537 {
538 int cpos;
539 int ll = strlen(line);
540
541 del_end = 0;
542 cpos = get_motion();
543 if (cpos == ll || linelim == ll)
544 del_end = 1;
545 del_chars(cpos, linelim);
546 }
547
548 static void
change_cmd()549 change_cmd()
550 {
551 delete_cmd();
552 if (del_end)
553 append_line();
554 else
555 insert_mode();
556 }
557
558 static void
del_chars(first,last)559 del_chars(first, last)
560 register int first, last;
561 {
562 int temp;
563
564 if (first == last)
565 return;
566
567 if (last < first) {
568 temp = last; last = first; first = temp;
569 }
570
571 linelim = first;
572 while(first < last) {
573 del_in_line();
574 --last;
575 }
576 }
577
578 static void
del_to_end()579 del_to_end()
580 {
581 if (linelim < 0)
582 return;
583 line[linelim] = '\0';
584 linelim = back_line();
585 }
586
587 void
cr_line()588 cr_line()
589 {
590 insert_mode();
591 if (linelim != -1) {
592 showrange = 0;
593 save_hist();
594 linelim = 0;
595 (void) yyparse ();
596 linelim = -1;
597 }
598 else /* '\n' alone will put you into insert mode */
599 { *line = '\0';
600 linelim = 0;
601 }
602 }
603
604 /* History functions */
605
606 static void
save_hist()607 save_hist()
608 {
609 if (lasthist < 0)
610 { lasthist = 0;
611 }
612 else
613 lasthist = (lasthist + 1) % HISTLEN;
614
615 if (lasthist > endhist)
616 endhist = lasthist;
617
618 if (history[lasthist].len < strlen(line)+1)
619 { history[lasthist].len = strlen(line)+40;
620 history[lasthist].histline = scxrealloc(history[lasthist].histline,
621 history[lasthist].len);
622 }
623 (void) strcpy(history[lasthist].histline, line);
624 }
625
626 static void
back_hist()627 back_hist()
628 {
629 if (histp == -1)
630 histp = lasthist;
631 else
632 if (histp == 0)
633 { if (endhist != lasthist)
634 histp = endhist;
635 }
636 else
637 if (histp != ((lasthist + 1) % (endhist + 1)))
638 histp--;
639
640 if (lasthist < 0)
641 line[linelim = 0] = '\0';
642 else {
643 (void) strcpy(line, history[histp].histline);
644 linelim = 0;
645 }
646 }
647
648 static void
search_hist()649 search_hist()
650 {
651 static unsigned lastsrchlen = 0;
652
653 if(linelim < 1) {
654 linelim = 0;
655 edit_mode();
656 return;
657 }
658
659 if (strlen(line)+1 > lastsrchlen)
660 { lastsrchlen = strlen(line)+40;
661 last_search = scxrealloc(last_search, lastsrchlen);
662 }
663 (void)strcpy(last_search, line+1);
664 search_again();
665 mode = EDIT_MODE;
666 }
667
668 static void
search_again()669 search_again()
670 {
671 int found_it;
672 int do_next;
673 int prev_histp;
674 char *look_here;
675
676 prev_histp = histp;
677 if ((last_search == NULL) || (*last_search == '\0'))
678 return;
679
680 do {
681 back_hist();
682 if (prev_histp == histp)
683 break;
684 prev_histp = histp;
685 look_here = line;
686 found_it = do_next = 0;
687 for ( look_here = strchr(look_here, last_search[0]);
688 look_here != NULL && !found_it && !do_next;
689 look_here = strchr(look_here, last_search[0]) )
690 {
691 if (strncmp(look_here, last_search, strlen(last_search)) == 0)
692 found_it++;
693 else if (look_here < line + strlen(line) - 1)
694 look_here++;
695 else
696 do_next++;
697 }
698 } while (!found_it);
699 }
700
701 static void
for_hist()702 for_hist()
703 {
704 if (histp == -1)
705 histp = lasthist;
706 else
707 if (histp != lasthist)
708 histp = (histp + 1) % (endhist + 1);
709
710 if (lasthist < 0)
711 line[linelim = 0] = '\0';
712 else {
713 (void) strcpy(line, history[histp].histline);
714 linelim = 0;
715 }
716 }
717
718 static void
col_0()719 col_0()
720 {
721 linelim = 0;
722 }
723
724 static void
last_col()725 last_col()
726 {
727 linelim = strlen(line);
728 if (linelim > 0)
729 --linelim;
730 }
731
732 static int
find_char()733 find_char()
734 {
735 register int c;
736 register int i;
737
738
739 c = vigetch();
740 i = linelim;
741 while(line[i] && line[i] != c)
742 i++;
743 if (!line[i])
744 i = linelim;
745 return(i);
746 }
747
748 static int
to_char()749 to_char()
750 {
751 register int i;
752
753 i = find_char();
754 if (i > 0 && i != linelim)
755 --i;
756
757 return(i);
758 }
759
760
761 char *
get_str(s,max_str_len)762 get_str(s, max_str_len)
763 char *s; /* prompt and returned string */
764 int max_str_len;
765 {
766 static char buf[1024]; /* hold the characters as they are typed */
767 #ifdef HAVE_X11_X_H
768 int count=0; /* how many characters have been entered */
769 int maxcount; /* the max number of chars to be entered */
770 int done=0; /* true when input is finished */
771 int slen; /* length of prompt string */
772 XEvent event; /* input event structure */
773 char keystr[3]; /* ASCII version of keypress */
774
775 if (using_X)
776 {
777 clearlines(0,0);
778 slen = strlen(s);
779 max_str_len--; /* decrease this to save room for null byte */
780 maxcount = maintextcols - slen;
781 maxcount = ((maxcount < max_str_len) ? maxcount : max_str_len);
782 buf[0]='_'; /* the "cursor" */
783
784 if (slen)
785 XDrawImageString(dpy,mainwin,maingc, textcol(0), textrow(0), s, slen);
786
787 XDrawImageString(dpy,mainwin,maingc, textcol(slen+1), textrow(0), "_", 1 );
788 while (!done){
789 XNextEvent(dpy, &event);
790 switch(event.type){
791
792 case Expose:
793 update(FALSE);
794 if (slen)
795 XDrawImageString(dpy, mainwin, maingc,
796 textcol(0), textrow(0), s, slen);
797 if (count)
798 XDrawImageString(dpy, mainwin, maingc,
799 textcol(slen), textrow(0), buf, count);
800
801 XDrawImageString(dpy, mainwin, maingc,
802 textcol(slen+count), textrow(0), "_", 1 );
803 break;
804
805 case MappingNotify:
806 XRefreshKeyboardMapping(&(event.xmapping));
807 break;
808
809 case ConfigureNotify:
810 sc_handleresize(&event);
811 maxcount = maintextcols - slen;
812 maxcount = ((maxcount < max_str_len) ? maxcount : max_str_len);
813 break;
814
815 case KeyPress:
816 if (XLookupString(&(event.xkey), keystr, 3, 0, 0)){
817 switch( keystr[0]){
818 case 10: /* linefeed */
819 case 13: /* carriage return */
820 done = 1;
821 break;
822 case ctl('h'): /* backspace */
823 case ctl('?'): /* delete */
824 case DEL:
825 if (count){
826 buf[--count]='_';
827 XDrawImageString(dpy,mainwin,maingc,
828 textcol(slen+count+1),textrow(0), "_ ", 2);
829 } else {
830 XBell(dpy,50);
831 /*fprintf(stderr,"\007");*/ /* bell */
832 }
833 break;
834
835 default:
836 if ((keystr[0]>=32) && (keystr[0]<127)){
837 if (count<maxcount){
838 buf[count++]=keystr[0];
839 buf[count]='_';
840 XDrawImageString(dpy,mainwin,maingc,
841 textcol(slen+count),textrow(0),
842 buf+count-1, 2);
843 } else
844 XBell(dpy,50);
845 /*fprintf(stderr,"\007");*/
846 } else
847 XBell(dpy,50);
848 /*fprintf(stderr,"\007");*/
849 break;
850
851 } /* switch keystr[0] */
852 } /* if XLookupString */
853 } /* switch event.type */
854 } /* while !done */
855 buf[count] = 0;
856 strcpy(s, buf);
857 clearlines(0,0);
858 show_top_line();
859 return s;
860 } else
861 #endif /* HAVE_X11_X_H */
862 {
863 move(0,0);
864 clrtoeol();
865 printw("%s", s);
866 refresh();
867
868 echo();
869 getstr(buf);
870 noecho();
871 strncpy(s, buf, max_str_len);
872 s[max_str_len] = '\0';
873 move(0,0);
874 clrtoeol();
875 show_top_line();
876 return(s);
877 } /* HAVE_X11_X_H, end curses */
878 }
879
880