source: trunk/minix/commands/elvis/vcmd.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 16.9 KB
Line 
1/* vcmd.c */
2
3/* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
8 */
9
10
11/* This file contains the functions that handle VI commands */
12
13
14#include "config.h"
15#include "ctype.h"
16#include "vi.h"
17#if MSDOS
18# include <process.h>
19# include <string.h>
20#endif
21#if TOS
22# include <osbind.h>
23# include <string.h>
24#endif
25#if OSK
26# include <stdio.h>
27#endif
28
29
30/* This function puts the editor in EX mode */
31MARK v_quit()
32{
33 move(LINES - 1, 0);
34 mode = MODE_EX;
35 return cursor;
36}
37
38/* This function causes the screen to be redrawn */
39MARK v_redraw()
40{
41 redraw(MARK_UNSET, FALSE);
42 return cursor;
43}
44
45/* This function executes a string of EX commands, and waits for a user keystroke
46 * before returning to the VI screen. If that keystroke is another ':', then
47 * another EX command is read and executed.
48 */
49/*ARGSUSED*/
50MARK v_1ex(m, text)
51 MARK m; /* the current line */
52 char *text; /* the first command to execute */
53{
54 /* run the command. be careful about modes & output */
55 exwrote = (mode == MODE_COLON);
56 doexcmd(text);
57 exrefresh();
58
59 /* if mode is no longer MODE_VI, then we should quit right away! */
60 if (mode != MODE_VI && mode != MODE_COLON)
61 return cursor;
62
63 /* The command did some output. Wait for a keystoke. */
64 if (exwrote)
65 {
66 mode = MODE_VI;
67 msg("[Hit <RETURN> to continue]");
68 if (getkey(0) == ':')
69 { mode = MODE_COLON;
70 addch('\n');
71 }
72 else
73 redraw(MARK_UNSET, FALSE);
74 }
75
76 return cursor;
77}
78
79/* This function undoes the last change */
80/*ARGSUSED*/
81MARK v_undo(m)
82 MARK m; /* (ignored) */
83{
84 if (undo())
85 {
86 redraw(MARK_UNSET, FALSE);
87 }
88 return cursor;
89}
90
91/* This function deletes the character(s) that the cursor is on */
92MARK v_xchar(m, cnt, cmd)
93 MARK m; /* where to start deletions */
94 long cnt; /* number of chars to delete */
95 int cmd; /* either 'x' or 'X' */
96{
97 DEFAULT(1);
98
99 /* for 'X', adjust so chars are deleted *BEFORE* cursor */
100 if (cmd == 'X')
101 {
102 if (markidx(m) < cnt)
103 return MARK_UNSET;
104 m -= cnt;
105 }
106
107 /* make sure we don't try to delete more thars than there are */
108 pfetch(markline(m));
109 if (markidx(m + cnt) > plen)
110 {
111 cnt = plen - markidx(m);
112 }
113 if (cnt == 0L)
114 {
115 return MARK_UNSET;
116 }
117
118 /* do it */
119 ChangeText
120 {
121 cut(m, m + cnt);
122 delete(m, m + cnt);
123 }
124 return m;
125}
126
127/* This function defines a mark */
128/*ARGSUSED*/
129MARK v_mark(m, count, key)
130 MARK m; /* where the mark will be */
131 long count; /* (ignored) */
132 int key; /* the ASCII label of the mark */
133{
134 if (key < 'a' || key > 'z')
135 {
136 msg("Marks must be from a to z");
137 }
138 else
139 {
140 mark[key - 'a'] = m;
141 }
142 return m;
143}
144
145/* This function toggles upper & lower case letters */
146MARK v_ulcase(m, cnt)
147 MARK m; /* where to make the change */
148 long cnt; /* number of chars to flip */
149{
150 REG char *pos;
151 REG int j;
152
153 DEFAULT(1);
154
155 /* fetch the current version of the line */
156 pfetch(markline(m));
157
158 /* for each position in the line */
159 for (j = 0, pos = &ptext[markidx(m)]; j < cnt && *pos; j++, pos++)
160 {
161 if (isupper(*pos))
162 {
163 tmpblk.c[j] = tolower(*pos);
164 }
165 else
166 {
167 tmpblk.c[j] = toupper(*pos);
168 }
169 }
170
171 /* if the new text is different from the old, then change it */
172 if (strncmp(tmpblk.c, &ptext[markidx(m)], j))
173 {
174 ChangeText
175 {
176 tmpblk.c[j] = '\0';
177 change(m, m + j, tmpblk.c);
178 }
179 }
180
181 return m + j;
182}
183
184
185MARK v_replace(m, cnt, key)
186 MARK m; /* first char to be replaced */
187 long cnt; /* number of chars to replace */
188 int key; /* what to replace them with */
189{
190 REG char *text;
191 REG int i;
192
193 DEFAULT(1);
194
195 /* map ^M to '\n' */
196 if (key == '\r')
197 {
198 key = '\n';
199 }
200
201 /* make sure the resulting line isn't too long */
202 if (cnt > BLKSIZE - 2 - markidx(m))
203 {
204 cnt = BLKSIZE - 2 - markidx(m);
205 }
206
207 /* build a string of the desired character with the desired length */
208 for (text = tmpblk.c, i = cnt; i > 0; i--)
209 {
210 *text++ = key;
211 }
212 *text = '\0';
213
214 /* make sure cnt doesn't extend past EOL */
215 pfetch(markline(m));
216 key = markidx(m);
217 if (key + cnt > plen)
218 {
219 cnt = plen - key;
220 }
221
222 /* do the replacement */
223 ChangeText
224 {
225 change(m, m + cnt, tmpblk.c);
226 }
227
228 if (*tmpblk.c == '\n')
229 {
230 return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE;
231 }
232 else
233 {
234 return m + cnt - 1;
235 }
236}
237
238MARK v_overtype(m)
239 MARK m; /* where to start overtyping */
240{
241 MARK end; /* end of a substitution */
242 static long width; /* width of a single-line replace */
243
244 /* the "doingdot" version of replace is really a substitution */
245 if (doingdot)
246 {
247 /* was the last one really repeatable? */
248 if (width < 0)
249 {
250 msg("Can't repeat a multi-line overtype command");
251 return MARK_UNSET;
252 }
253
254 /* replacing nothing by nothing? Don't bother */
255 if (width == 0)
256 {
257 return m;
258 }
259
260 /* replace some chars by repeated text */
261 return v_subst(m, width);
262 }
263
264 /* Normally, we input starting here, in replace mode */
265 ChangeText
266 {
267 end = input(m, m, WHEN_VIREP, FALSE);
268 }
269
270 /* if we ended on the same line we started on, then this
271 * overtype is repeatable via the dot key.
272 */
273 if (markline(end) == markline(m) && end >= m - 1L)
274 {
275 width = end - m + 1L;
276 }
277 else /* it isn't repeatable */
278 {
279 width = -1L;
280 }
281
282 return end;
283}
284
285
286/* This function selects which cut buffer to use */
287/*ARGSUSED*/
288MARK v_selcut(m, cnt, key)
289 MARK m;
290 long cnt;
291 int key;
292{
293 cutname(key);
294 return m;
295}
296
297/* This function pastes text from a cut buffer */
298/*ARGSUSED*/
299MARK v_paste(m, cnt, cmd)
300 MARK m; /* where to paste the text */
301 long cnt; /* (ignored) */
302 int cmd; /* either 'p' or 'P' */
303{
304 MARK dest;
305
306 ChangeText
307 {
308 /* paste the text, and find out where it ends */
309 dest = paste(m, cmd == 'p', TRUE);
310
311 /* was that a line-mode paste? */
312 if (dest && markline(dest) != markline(m))
313 {
314 /* line-mode pastes leave the cursor at the front
315 * of the first pasted line.
316 */
317 dest = m;
318 if (cmd == 'p')
319 {
320 dest += BLKSIZE;
321 }
322 force_flags |= FRNT;
323 }
324 }
325 return dest;
326}
327
328/* This function yanks text into a cut buffer */
329MARK v_yank(m, n)
330 MARK m, n; /* range of text to yank */
331{
332 cut(m, n);
333 return m;
334}
335
336/* This function deletes a range of text */
337MARK v_delete(m, n)
338 MARK m, n; /* range of text to delete */
339{
340 /* illegal to try and delete nothing */
341 if (n <= m)
342 {
343 return MARK_UNSET;
344 }
345
346 /* Do it */
347 ChangeText
348 {
349 cut(m, n);
350 delete(m, n);
351 }
352 return m;
353}
354
355
356/* This starts input mode without deleting anything */
357MARK v_insert(m, cnt, key)
358 MARK m; /* where to start (sort of) */
359 long cnt; /* repeat how many times? */
360 int key; /* what command is this for? {a,A,i,I,o,O} */
361{
362 int wasdot;
363 long reps;
364 int above; /* boolean: new line going above old line? */
365
366 DEFAULT(1);
367
368 ChangeText
369 {
370 /* tweak the insertion point, based on command key */
371 above = FALSE;
372 switch (key)
373 {
374 case 'i':
375 break;
376
377 case 'a':
378 pfetch(markline(m));
379 if (plen > 0)
380 {
381 m++;
382 }
383 break;
384
385 case 'I':
386 m = m_front(m, 1L);
387 break;
388
389 case 'A':
390 pfetch(markline(m));
391 m = (m & ~(BLKSIZE - 1)) + plen;
392 break;
393
394 case 'O':
395 m &= ~(BLKSIZE - 1);
396 add(m, "\n");
397 above = TRUE;
398 break;
399
400 case 'o':
401 m = (m & ~(BLKSIZE - 1)) + BLKSIZE;
402 add(m, "\n");
403 break;
404 }
405
406 /* insert the same text once or more */
407 for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE)
408 {
409 m = input(m, m, WHEN_VIINP, above) + 1;
410 }
411 if (markidx(m) > 0)
412 {
413 m--;
414 }
415
416 doingdot = wasdot;
417 }
418
419#ifndef CRUNCH
420# ifndef NO_EXTENSIONS
421 if (key == 'i' && *o_inputmode && mode == MODE_VI)
422 {
423 msg("Now in command mode! To return to input mode, hit <i>");
424 }
425# endif
426#endif
427
428 return m;
429}
430
431/* This starts input mode with some text deleted */
432MARK v_change(m, n)
433 MARK m, n; /* the range of text to change */
434{
435 int lnmode; /* is this a line-mode change? */
436
437 /* swap them if they're in reverse order */
438 if (m > n)
439 {
440 MARK tmp;
441 tmp = m;
442 m = n;
443 n = tmp;
444 }
445
446 /* for line mode, retain the last newline char */
447 lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
448 if (lnmode)
449 {
450 n -= BLKSIZE;
451 pfetch(markline(n));
452 n = (n & ~(BLKSIZE - 1)) + plen;
453 }
454
455 ChangeText
456 {
457 cut(m, n);
458 m = input(m, n, WHEN_VIINP, FALSE);
459 }
460
461 return m;
462}
463
464/* This function replaces a given number of characters with input */
465MARK v_subst(m, cnt)
466 MARK m; /* where substitutions start */
467 long cnt; /* number of chars to replace */
468{
469 DEFAULT(1);
470
471 /* make sure we don't try replacing past EOL */
472 pfetch(markline(m));
473 if (markidx(m) + cnt > plen)
474 {
475 cnt = plen - markidx(m);
476 }
477
478 /* Go for it! */
479 ChangeText
480 {
481 cut(m, m + cnt);
482 m = input(m, m + cnt, WHEN_VIINP, FALSE);
483 }
484 return m;
485}
486
487/* This calls the ex "join" command to join some lines together */
488MARK v_join(m, cnt)
489 MARK m; /* the first line to be joined */
490 long cnt; /* number of other lines to join */
491{
492 MARK joint; /* where the lines were joined */
493
494 DEFAULT(1);
495
496 /* figure out where the joint will be */
497 pfetch(markline(m));
498 joint = (m & ~(BLKSIZE - 1)) + plen;
499
500 /* join the lines */
501 cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, "");
502
503 /* the cursor should be left at the joint */
504 return joint;
505}
506
507
508/* This calls the ex "<" command to shift some lines left */
509MARK v_lshift(m, n)
510 MARK m, n; /* range of lines to shift */
511{
512 /* adjust for inclusive endmarks in ex */
513 n -= BLKSIZE;
514
515 cmd_shift(m, n, CMD_SHIFTL, FALSE, (char *)0);
516
517 return m;
518}
519
520/* This calls the ex ">" command to shift some lines right */
521MARK v_rshift(m, n)
522 MARK m, n; /* range of lines to shift */
523{
524 /* adjust for inclusive endmarks in ex */
525 n -= BLKSIZE;
526
527 cmd_shift(m, n, CMD_SHIFTR, FALSE, (char *)0);
528
529 return m;
530}
531
532/* This filters some lines through a preset program, to reformat them */
533MARK v_reformat(m, n)
534 MARK m, n; /* range of lines to shift */
535{
536 /* adjust for inclusive endmarks in ex */
537 n -= BLKSIZE;
538
539 /* run the filter command */
540 filter(m, n, o_equalprg, TRUE);
541
542 redraw(MARK_UNSET, FALSE);
543 return m;
544}
545
546
547/* This runs some lines through a filter program */
548MARK v_filter(m, n)
549 MARK m, n; /* range of lines to shift */
550{
551 char cmdln[150]; /* a shell command line */
552
553 /* adjust for inclusive endmarks in ex */
554 n -= BLKSIZE;
555
556 if (vgets('!', cmdln, sizeof(cmdln)) > 0)
557 {
558 filter(m, n, cmdln, TRUE);
559 }
560
561 redraw(MARK_UNSET, FALSE);
562 return m;
563}
564
565
566/* This function runs the ex "file" command to show the file's status */
567MARK v_status()
568{
569 cmd_file(cursor, cursor, CMD_FILE, 0, "");
570 return cursor;
571}
572
573
574/* This function runs the ":&" command to repeat the previous :s// */
575MARK v_again(m, n)
576 MARK m, n;
577{
578 cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, "");
579 return cursor;
580}
581
582
583
584/* This function switches to the previous file, if possible */
585MARK v_switch()
586{
587 if (!*prevorig)
588 msg("No previous file");
589 else
590 { strcpy(tmpblk.c, prevorig);
591 cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c);
592 }
593 return cursor;
594}
595
596/* This function does a tag search on a keyword */
597/*ARGSUSED*/
598MARK v_tag(keyword, m, cnt)
599 char *keyword;
600 MARK m;
601 long cnt;
602{
603 /* move the cursor to the start of the tag name, where m is */
604 cursor = m;
605
606 /* perform the tag search */
607 cmd_tag(cursor, cursor, CMD_TAG, 0, keyword);
608
609 return cursor;
610}
611
612#ifndef NO_EXTENSIONS
613/* This function looks up a keyword by calling the helpprog program */
614/*ARGSUSED*/
615MARK v_keyword(keyword, m, cnt)
616 char *keyword;
617 MARK m;
618 long cnt;
619{
620 int waswarn;
621 char cmdline[130];
622
623 move(LINES - 1, 0);
624 addstr("---------------------------------------------------------\n");
625 clrtoeol();
626 refresh();
627 sprintf(cmdline, "%s %s", o_keywordprg, keyword);
628 waswarn = *o_warn;
629 *o_warn = FALSE;
630 suspend_curses();
631 if (system(cmdline))
632 {
633 addstr("<<< failed >>>\n");
634 }
635 resume_curses(FALSE);
636 mode = MODE_VI;
637 redraw(MARK_UNSET, FALSE);
638 *o_warn = waswarn;
639
640 return m;
641}
642
643
644
645MARK v_increment(keyword, m, cnt)
646 char *keyword;
647 MARK m;
648 long cnt;
649{
650 static sign;
651 char newval[12];
652 long atol();
653
654 DEFAULT(1);
655
656 /* get one more keystroke, unless doingdot */
657 if (!doingdot)
658 {
659 sign = getkey(0);
660 }
661
662 /* adjust the number, based on that second keystroke */
663 switch (sign)
664 {
665 case '+':
666 case '#':
667 cnt = atol(keyword) + cnt;
668 break;
669
670 case '-':
671 cnt = atol(keyword) - cnt;
672 break;
673
674 case '=':
675 break;
676
677 default:
678 return MARK_UNSET;
679 }
680 sprintf(newval, "%ld", cnt);
681
682 ChangeText
683 {
684 change(m, m + strlen(keyword), newval);
685 }
686
687 return m;
688}
689#endif
690
691
692/* This function acts like the EX command "xit" */
693/*ARGSUSED*/
694MARK v_xit(m, cnt, key)
695 MARK m; /* ignored */
696 long cnt; /* ignored */
697 int key; /* must be a second 'Z' */
698{
699 /* if second char wasn't 'Z', fail */
700 if (key != 'Z')
701 {
702 return MARK_UNSET;
703 }
704
705 /* move the cursor to the bottom of the screen */
706 move(LINES - 1, 0);
707 clrtoeol();
708
709 /* do the xit command */
710 cmd_xit(m, m, CMD_XIT, FALSE, "");
711
712 /* return the cursor */
713 return m;
714}
715
716
717/* This function undoes changes to a single line, if possible */
718MARK v_undoline(m)
719 MARK m; /* where we hope to undo the change */
720{
721 /* make sure we have the right line in the buffer */
722 if (markline(m) != U_line)
723 {
724 return MARK_UNSET;
725 }
726
727 /* fix it */
728 ChangeText
729 {
730 strcat(U_text, "\n");
731 change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text);
732 }
733
734 /* nothing in the buffer anymore */
735 U_line = -1L;
736
737 /* return, with the cursor at the front of the line */
738 return m & ~(BLKSIZE - 1);
739}
740
741
742#ifndef NO_ERRLIST
743MARK v_errlist(m)
744 MARK m;
745{
746 cmd_errlist(m, m, CMD_ERRLIST, FALSE, "");
747 return cursor;
748}
749#endif
750
751
752#ifndef NO_AT
753/*ARGSUSED*/
754MARK v_at(m, cnt, key)
755 MARK m;
756 long cnt;
757 int key;
758{
759 int size;
760
761 size = cb2str(key, tmpblk.c, BLKSIZE);
762 if (size <= 0 || size == BLKSIZE)
763 {
764 return MARK_UNSET;
765 }
766
767 execmap(0, tmpblk.c, FALSE);
768 return cursor;
769}
770#endif
771
772
773#ifdef SIGTSTP
774MARK v_suspend()
775{
776 cmd_suspend(MARK_UNSET, MARK_UNSET, CMD_SUSPEND, FALSE, "");
777 return MARK_UNSET;
778}
779#endif
780
781
782#ifndef NO_VISIBLE
783/*ARGSUSED*/
784MARK v_start(m, cnt, cmd)
785 MARK m; /* starting point for a v or V command */
786 long cnt; /* ignored */
787 int cmd; /* either 'v' or 'V' */
788{
789 if (V_from)
790 {
791 V_from = MARK_UNSET;
792 }
793 else
794 {
795 V_from = m;
796 V_linemd = isupper(cmd);
797 }
798 return m;
799}
800#endif
801
802#ifndef NO_POPUP
803# define MENU_HEIGHT 11
804# define MENU_WIDTH 23
805MARK v_popup(m, n)
806 MARK m, n; /* the range of text to change */
807{
808 int i;
809 int y, x; /* position where the window will pop up at */
810 int key; /* keystroke from the user */
811 int sel; /* index of the selected operation */
812 static int dfltsel;/* default value of sel */
813 static char *labels[11] =
814 {
815 "ESC cancel! ",
816 " d delete (cut) ",
817 " y yank (copy) ",
818 " p paste after ",
819 " P paste before ",
820 " > more indented ",
821 " < less indented ",
822 " = reformat ",
823 " ! external filter ",
824 " ZZ save & exit ",
825 " u undo previous "
826 };
827
828 /* try to position the menu near the cursor */
829 x = physcol - (MENU_WIDTH / 2);
830 if (x < 0)
831 x = 0;
832 else if (x + MENU_WIDTH + 2 > COLS)
833 x = COLS - MENU_WIDTH - 2;
834 if (markline(cursor) < topline || markline(cursor) > botline)
835 y = 0;
836 else if (markline(cursor) + 1L + MENU_HEIGHT > botline)
837 y = (int)(markline(cursor) - topline) - MENU_HEIGHT;
838 else
839 y = (int)(markline(cursor) - topline) + 1L;
840
841 /* draw the menu */
842 for (sel = 0; sel < MENU_HEIGHT; sel++)
843 {
844 move(y + sel, x);
845 do_POPUP();
846 if (sel == dfltsel)
847 qaddstr("-->");
848 else
849 qaddstr(" ");
850 qaddstr(labels[sel]);
851 do_SE();
852 }
853
854 /* get a selection */
855 move(y + dfltsel, x + 4);
856 for (sel = dfltsel; (key = getkey(WHEN_POPUP)) != '\\' && key != '\r'; )
857 {
858 /* erase the selection arrow */
859 move(y + sel, x);
860 do_POPUP();
861 qaddstr(" ");
862 qaddstr(labels[sel]);
863 do_SE();
864
865 /* process the user's keystroke */
866 if (key == 'j' && sel < MENU_HEIGHT - 1)
867 {
868 sel++;
869 }
870 else if (key == 'k' && sel > 0)
871 {
872 sel--;
873 }
874 else if (key == '\033')
875 {
876 sel = 0;
877 break;
878 }
879 else
880 {
881 for (i = 1; i < MENU_HEIGHT && labels[i][1] != key; i++)
882 {
883 }
884 if (i < MENU_HEIGHT)
885 {
886 sel = i;
887 break;
888 }
889 }
890
891 /* redraw the arrow, possibly in a new place */
892 move(y + sel, x);
893 do_POPUP();
894 qaddstr("-->");
895 qaddstr(labels[sel]);
896 do_SE();
897 move(y + sel, x + 4);
898 }
899
900 /* in most cases, the default selection is "paste after" */
901 dfltsel = 3;
902
903 /* perform the appropriate action */
904 switch (sel)
905 {
906 case 0:
907 m = cursor;
908 dfltsel = 0;
909 break;
910
911 case 1: /* delete (cut) */
912 m = v_delete(m, n);
913 break;
914
915 case 2: /* yank (copy) */
916 m = v_yank(m, n);
917 break;
918
919 case 3: /* paste after */
920 m = v_paste(n, 1L, 'P');
921 break;
922
923 case 4: /* paste before */
924 m = v_paste(m, 1L, 'P');
925 dfltsel = 4;
926 break;
927
928 case 5: /* more indented */
929 m = v_rshift(m, n);
930 dfltsel = 5;
931 break;
932
933 case 6: /* less indented */
934 m = v_lshift(m, n);
935 dfltsel = 6;
936 break;
937
938 case 7: /* reformat */
939 m = v_reformat(m, n);
940 dfltsel = 7;
941 break;
942
943 case 8: /* external filter */
944 m = v_filter(m, n);
945 dfltsel = 0;
946 break;
947
948 case 9: /* save & exit */
949 /* get confirmation first */
950 do
951 {
952 key = getkey(0);
953 } while (key != '\\' && key != 'Z' && key != '\r' /* good */
954 && key != '\033'); /* bad */
955 if (key != '\033')
956 {
957 m = v_xit(m, 0L, 'Z');
958 }
959 break;
960
961 case 10: /* undo previous */
962 m = v_undo(m);
963 dfltsel = 9;
964 break;
965 }
966
967 /* arrange for the menu to be erased (except that "chg from kbd"
968 * already erased it, and "save & exit" doesn't care)
969 */
970 if (sel != 5 && sel != 9)
971 redraw(MARK_UNSET, FALSE);
972
973 return m;
974}
975#endif /* undef NO_POPUP */
Note: See TracBrowser for help on using the repository browser.