This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / elvis / input.c
CommitLineData
15637ed4
RG
1/* input.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 input() function, which implements vi's INPUT mode.
12 * It also contains the code that supports digraphs.
13 */
14
15#include "config.h"
16#include "ctype.h"
17#include "vi.h"
18
19
20#ifndef NO_DIGRAPH
21static struct _DIG
22{
23 struct _DIG *next;
24 char key1;
25 char key2;
26 char dig;
27 char save;
28} *digs;
29
30char digraph(key1, key2)
08746e8b
AM
31 int key1; /* the underlying character */
32 int key2; /* the second character */
15637ed4
RG
33{
34 int newkey;
35 REG struct _DIG *dp;
36
37 /* if digraphs are disabled, then just return the new char */
38 if (!*o_digraph)
39 {
40 return key2;
41 }
42
43 /* remember the new key, so we can return it if this isn't a digraph */
44 newkey = key2;
45
46 /* sort key1 and key2, so that their original order won't matter */
47 if (key1 > key2)
48 {
49 key2 = key1;
50 key1 = newkey;
51 }
52
53 /* scan through the digraph chart */
54 for (dp = digs;
55 dp && (dp->key1 != key1 || dp->key2 != key2);
56 dp = dp->next)
57 {
58 }
59
60 /* if this combination isn't in there, just use the new key */
61 if (!dp)
62 {
63 return newkey;
64 }
65
66 /* else use the digraph key */
67 return dp->dig;
68}
69
70/* this function lists or defines digraphs */
71void do_digraph(bang, extra)
72 int bang;
73 char extra[];
74{
75 int dig;
76 REG struct _DIG *dp;
77 struct _DIG *prev;
78 static int user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
79 char listbuf[8];
80
81 /* if "extra" is NULL, then we've reached the end of the built-ins */
82 if (!extra)
83 {
84 user_defined = TRUE;
85 return;
86 }
87
88 /* if no args, then display the existing digraphs */
89 if (*extra < ' ')
90 {
91 listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
92 listbuf[7] = '\0';
93 for (dig = 0, dp = digs; dp; dp = dp->next)
94 {
95 if (dp->save || bang)
96 {
97 dig += 7;
98 if (dig >= COLS)
99 {
100 addch('\n');
101 exrefresh();
102 dig = 7;
103 }
104 listbuf[3] = dp->key1;
105 listbuf[4] = dp->key2;
106 listbuf[6] = dp->dig;
107 qaddstr(listbuf);
108 }
109 }
110 addch('\n');
111 exrefresh();
112 return;
113 }
114
115 /* make sure we have at least two characters */
116 if (!extra[1])
117 {
118 msg("Digraphs must be composed of two characters");
119 return;
120 }
121
122 /* sort key1 and key2, so that their original order won't matter */
123 if (extra[0] > extra[1])
124 {
125 dig = extra[0];
126 extra[0] = extra[1];
127 extra[1] = dig;
128 }
129
130 /* locate the new digraph character */
131 for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
132 {
133 }
134 dig = extra[dig];
135 if (!bang && dig)
136 {
137 dig |= 0x80;
138 }
139
140 /* search for the digraph */
141 for (prev = (struct _DIG *)0, dp = digs;
142 dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
143 prev = dp, dp = dp->next)
144 {
145 }
146
147 /* deleting the digraph? */
148 if (!dig)
149 {
150 if (!dp)
151 {
152#ifndef CRUNCH
153 msg("%c%c not a digraph", extra[0], extra[1]);
154#endif
155 return;
156 }
157 if (prev)
158 prev->next = dp->next;
159 else
160 digs = dp->next;
08746e8b 161 _free_(dp);
15637ed4
RG
162 return;
163 }
164
165 /* if necessary, create a new digraph struct for the new digraph */
166 if (dig && !dp)
167 {
168 dp = (struct _DIG *)malloc(sizeof *dp);
169 if (!dp)
170 {
171 msg("Out of space in the digraph table");
172 return;
173 }
174 if (prev)
175 prev->next = dp;
176 else
177 digs = dp;
178 dp->next = (struct _DIG *)0;
179 }
180
181 /* assign it the new digraph value */
182 dp->key1 = extra[0];
183 dp->key2 = extra[1];
184 dp->dig = dig;
185 dp->save = user_defined;
186}
187
188# ifndef NO_MKEXRC
189void savedigs(fd)
190 int fd;
191{
192 static char buf[] = "digraph! XX Y\n";
193 REG struct _DIG *dp;
194
195 for (dp = digs; dp; dp = dp->next)
196 {
197 if (dp->save)
198 {
199 buf[9] = dp->key1;
200 buf[10] = dp->key2;
201 buf[12] = dp->dig;
202 write(fd, buf, (unsigned)14);
203 }
204 }
205}
206# endif
207#endif
208
209
210/* This function allows the user to replace an existing (possibly zero-length)
211 * chunk of text with typed-in text. It returns the MARK of the last character
212 * that the user typed in.
213 */
08746e8b 214MARK input(from, to, when, delta)
15637ed4
RG
215 MARK from; /* where to start inserting text */
216 MARK to; /* extent of text to delete */
217 int when; /* either WHEN_VIINP or WHEN_VIREP */
08746e8b 218 int delta; /* 1 to take indent from lower line, -1 for upper, 0 for none */
15637ed4
RG
219{
220 char key[2]; /* key char followed by '\0' char */
221 char *build; /* used in building a newline+indent string */
222 char *scan; /* used while looking at the indent chars of a line */
223 MARK m; /* some place in the text */
224#ifndef NO_EXTENSIONS
225 int quit = FALSE; /* boolean: are we exiting after this? */
226 int inchg; /* boolean: have we done a "beforedo()" yet? */
227#endif
228
229#ifdef DEBUG
230 /* if "from" and "to" are reversed, complain */
231 if (from > to)
232 {
233 msg("ERROR: input(%ld:%d, %ld:%d)",
234 markline(from), markidx(from),
235 markline(to), markidx(to));
236 return MARK_UNSET;
237 }
238#endif
239
240 key[1] = 0;
241
242 /* if we're replacing text with new text, save the old stuff */
243 /* (Alas, there is no easy way to save text for replace mode) */
244 if (from != to)
245 {
246 cut(from, to);
247 }
248
249 /* if doing a dot command, then reuse the previous text */
250 if (doingdot)
251 {
252 ChangeText
253 {
254 /* delete the text that's there now */
255 if (from != to)
256 {
257 delete(from, to);
258 }
259
260 /* insert the previous text */
261 cutname('.');
262 cursor = paste(from, FALSE, TRUE) + 1L;
263 }
264 }
265 else /* interactive version */
266 {
267 /* assume that whoever called this already did a beforedo() */
268#ifndef NO_EXTENSIONS
269 inchg = TRUE;
270#endif
271
272 /* if doing a change within the line... */
273 if (from != to && markline(from) == markline(to))
274 {
275 /* mark the end of the text with a "$" */
276 change(to - 1, to, "$");
277 }
278 else
279 {
280 /* delete the old text right off */
281 if (from != to)
282 {
283 delete(from, to);
284 }
285 to = from;
286 }
287
288 /* handle autoindent of the first line, maybe */
289 cursor = from;
08746e8b
AM
290 m = cursor + MARK_AT_LINE(delta);
291 if (delta != 0 && *o_autoindent && markidx(m) == 0
15637ed4
RG
292 && markline(m) >= 1L && markline(m) <= nlines)
293 {
294 /* Only autoindent blank lines. */
295 pfetch(markline(cursor));
296 if (plen == 0)
297 {
298 /* Okay, we really want to autoindent */
299 pfetch(markline(m));
300 for (scan = ptext, build = tmpblk.c;
301 *scan == ' ' || *scan == '\t';
302 )
303 {
304 *build++ = *scan++;
305 }
306 if (build > tmpblk.c)
307 {
308 *build = '\0';
309 add(cursor, tmpblk.c);
08746e8b 310 cursor += (int)(build - tmpblk.c);
15637ed4
RG
311 if (cursor > to)
312 to = cursor;
313 }
314 }
315 }
316
317 /* repeatedly add characters from the user */
318 for (;;)
319 {
320 /* Get a character */
321 redraw(cursor, TRUE);
322#ifdef DEBUG2
323 msg("cursor=%ld.%d, to=%ld.%d",
324 markline(cursor), markidx(cursor),
325 markline(to), markidx(to));
326#endif
327#ifndef NO_ABBR
328 pfetch(markline(cursor));
329 build = ptext;
330 if (pline == markline(from))
331 build += markidx(from);
08746e8b 332 for (scan = ptext + markidx(cursor); --scan >= build && !isspace(*scan); )
15637ed4
RG
333 {
334 }
335 scan++;
336 key[0] = getabkey(when, scan, (int)(ptext + markidx(cursor) - scan));
337#else
338 key[0] = getkey(when);
339#endif
340#ifndef NO_VISIBLE
08746e8b 341 if (key[0] != ctrl('O') && V_from != MARK_UNSET)
15637ed4
RG
342 {
343 msg("Can't modify text during a selection");
344 beep();
345 continue;
346 }
347#endif
348
349#ifndef NO_EXTENSIONS
350 if (key[0] == ctrl('O'))
351 {
352 if (inchg)
353 {
354 if (cursor < to)
355 {
356 delete(cursor, to);
357 redraw(cursor, TRUE);
358 }
359 afterdo();
360 inchg = FALSE;
361 }
362 }
363 else if (key[0] != ctrl('['))
364 {
365 if (!inchg)
366 {
367 beforedo(FALSE);
368 inchg = TRUE;
369 }
370 }
371#endif
372
373#ifndef CRUNCH
374 /* if wrapmargin is set & we're past the
375 * warpmargin, then change the last whitespace
376 * characters on line into a newline
377 */
08746e8b 378 if (*o_wrapmargin)
15637ed4
RG
379 {
380 pfetch(markline(cursor));
08746e8b
AM
381 if (plen == idx2col(cursor, ptext, TRUE)
382 && plen > COLS - (*o_wrapmargin & 0xff))
15637ed4
RG
383 {
384 build = tmpblk.c;
385 *build++ = '\n';
386 if (*o_autoindent)
387 {
388 /* figure out indent for next line */
389 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
390 {
391 *build++ = *scan++;
392 }
393 }
394 *build = '\0';
395
396 scan = ptext + plen;
397 m = cursor & ~(BLKSIZE - 1);
398 while (ptext < scan)
399 {
400 scan--;
401 if (*scan != ' ' && *scan != '\t')
402 continue;
403
404 /*break up line, and we do autoindent if needed*/
08746e8b
AM
405 change(m + (int)(scan - ptext), m + (int)(scan - ptext) + 1, tmpblk.c);
406
407 /* NOTE: for some reason, MSC 5.10 doesn't
408 * like for these lines to be combined!!!
409 */
410 cursor = (cursor & ~(BLKSIZE - 1));
411 cursor += BLKSIZE;
412 cursor += strlen(tmpblk.c) - 1;
413 cursor += plen - (int)(scan - ptext) - 1;
15637ed4
RG
414
415 /*remove trailing spaces on previous line*/
416 pfetch(markline(m));
417 scan = ptext + plen;
418 while (ptext < scan)
419 {
420 scan--;
421 if (*scan != ' ' && *scan != '\t')
422 break;
423 }
08746e8b 424 delete(m + (int)(scan - ptext) + 1, m + plen);
15637ed4
RG
425
426 break;
427 }
428 }
429 }
430#endif /* !CRUNCH */
431
432 /* process it */
433 switch (*key)
434 {
435#ifndef NO_EXTENSIONS
436 case ctrl('O'): /* special movement mapped keys */
437 *key = getkey(0);
438 switch (*key)
439 {
440 case 'h': m = m_left(cursor, 0L); break;
441 case 'j':
442 case 'k': m = m_updnto(cursor, 0L, *key); break;
443 case 'l': m = cursor + 1; break;
444 case 'B':
445 case 'b': m = m_bword(cursor, 0L, *key); break;
446 case 'W':
447 case 'w': m = m_fword(cursor, 0L, *key, '\0'); break;
448 case '^': m = m_front(cursor, 0L); break;
449 case '$': m = m_rear(cursor, 0L); break;
450 case ctrl('B'):
451 case ctrl('F'):
452 m = m_scroll(cursor, 0L, *key); break;
453 case 'x':
454#ifndef NO_VISIBLE
455 if (V_from)
456 beep();
457 else
458#endif
459 ChangeText
460 {
461 m = v_xchar(cursor, 0L, 'x');
462 }
463 break;
464 case 'i': m = to = from = cursor;
465 when = WHEN_VIINP + WHEN_VIREP - when;
466 break;
467 case 'K':
468 pfetch(markline(cursor));
469 changes++; /* <- after this, we can alter ptext */
470 ptext[markidx(cursor)] = 0;
471 for (scan = ptext + markidx(cursor) - 1;
472 scan >= ptext && isalnum(*scan);
473 scan--)
474 {
475 }
476 scan++;
477 m = (*scan ? v_keyword(scan, cursor, 0L) : cursor);
478 break;
479
480# ifndef NO_VISIBLE
481 case 'v':
482 case 'V':
483 if (V_from)
484 V_from = MARK_UNSET;
485 else
486 V_from = cursor;
487 m = from = to = cursor;
488 V_linemd = (*key == 'V');
489 break;
490
491 case 'd':
492 case 'y':
493 case '\\':
494 /* do nothing if unmarked */
495 if (!V_from)
496 {
497 msg("You must mark the text first");
498 beep();
499 break;
500 }
501
502 /* "from" must come before "to" */
503 if (V_from < cursor)
504 {
505 from = V_from;
506 to = cursor;
507 }
508 else
509 {
510 from = cursor;
511 to = V_from;
512 }
513
514 /* we don't need V_from anymore */
515 V_from = MARK_UNSET;
516
517 if (V_linemd)
518 {
519 /* adjust for line mode */
520 from &= ~(BLKSIZE - 1);
521 to |= (BLKSIZE - 1);
522 }
523 else
524 {
525 /* in character mode, we must
526 * worry about deleting the newline
527 * at the end of the last line
528 */
529 pfetch(markline(to));
530 if (markidx(to) == plen)
531 to |= (BLKSIZE - 1);
532 }
533 to++;
534
535 switch (*key)
536 {
537 case 'y':
538 cut(from, to);
539 break;
540
541 case 'd':
542 ChangeText
543 {
544 cut(from, to);
545 delete(from, to);
546 }
547 cursor = from;
548 break;
549
550#ifndef NO_POPUP
551 case '\\':
552 ChangeText
553 {
554 cursor = v_popup(from, to);
555 }
556 break;
557#endif
558 }
559 m = from = to = cursor;
560 break;
561
562 case 'p':
563 case 'P':
564 V_from = MARK_UNSET;
565 ChangeText
566 {
08746e8b 567 m = paste(cursor, (*key == 'p'), FALSE);
15637ed4
RG
568 }
569 break;
570# endif /* !NO_VISIBLE */
571 default: m = MARK_UNSET;
572 }
573
574 /* adjust the moved cursor */
575 if (m != cursor)
576 {
08746e8b
AM
577 m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? NCOL|FINL : FINL));
578 if (plen && (*key == '$' || (*key == 'l' && m <= cursor)))
15637ed4
RG
579 {
580 m++;
581 }
582 }
583
584 /* if the cursor is reasonable, use it */
585 if (m == MARK_UNSET)
586 {
587 beep();
588 }
589 else
590 {
591 from = to = cursor = m;
592 }
593 break;
594
595 case ctrl('Z'):
596 if (getkey(0) == ctrl('Z'))
597 {
598 quit = TRUE;
599 goto BreakBreak;
600 }
601 break;
602#endif
603
604 case ctrl('['):
605 /* if last line contains only whitespace, then remove whitespace */
606 if (*o_autoindent)
607 {
608 pfetch(markline(cursor));
609 for (scan = ptext; isspace(*scan); scan++)
610 {
611 }
612 if (scan > ptext && !*scan)
613 {
614 cursor &= ~(BLKSIZE - 1L);
615 if (to < cursor + plen)
616 {
617 to = cursor + plen;
618 }
619 }
620 }
621 goto BreakBreak;
622
623 case ctrl('U'):
624 if (markline(cursor) == markline(from))
625 {
626 cursor = from;
627 }
628 else
629 {
630 cursor &= ~(BLKSIZE - 1);
631 }
632 break;
633
634 case ctrl('D'):
635 case ctrl('T'):
636 if (to > cursor)
637 {
638 delete(cursor, to);
639 }
640 mark[27] = cursor;
641 cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
642 if (mark[27])
643 {
644 cursor = mark[27];
645 }
646 else
647 {
648 cursor = m_front(cursor, 0L);
649 }
650 to = cursor;
651 break;
652
653 case '\b':
654 if (cursor <= from)
655 {
656 beep();
657 }
658 else if (markidx(cursor) == 0)
659 {
660 cursor -= BLKSIZE;
661 pfetch(markline(cursor));
662 cursor += plen;
663 }
664 else
665 {
666 cursor--;
667 }
668 break;
669
670 case ctrl('W'):
671 m = m_bword(cursor, 1L, 'b');
672 if (markline(m) == markline(cursor) && m >= from)
673 {
674 cursor = m;
675 if (from > cursor)
676 {
677 from = cursor;
678 }
679 }
680 else
681 {
682 beep();
683 }
684 break;
685
686 case '\n':
687#if OSK
688 case '\l':
689#else
690 case '\r':
691#endif
692 build = tmpblk.c;
693 *build++ = '\n';
694 if (*o_autoindent)
695 {
696 /* figure out indent for next line */
697 pfetch(markline(cursor));
698 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
699 {
700 *build++ = *scan++;
701 }
702
703 /* remove indent from this line, if blank */
08746e8b 704 if ((int)(scan - ptext) >= markidx(cursor) && plen > 0)
15637ed4
RG
705 {
706 to = cursor &= ~(BLKSIZE - 1);
08746e8b
AM
707 delete(cursor, cursor + (int)(scan - ptext));
708 }
709
710#if 0
711 /* advance "to" past whitespace at the cursor */
712 if (to >= cursor)
713 {
714 for (scan = ptext + markidx(cursor), to = cursor; *scan == ' ' || *scan == '\t'; scan++, to++)
715 {
716 }
15637ed4 717 }
08746e8b 718#endif
15637ed4
RG
719 }
720 *build = 0;
721 if (cursor >= to && when != WHEN_VIREP)
722 {
723 add(cursor, tmpblk.c);
724 }
725 else
726 {
727 change(cursor, to, tmpblk.c);
728 }
729 redraw(cursor, TRUE);
730 to = cursor = (cursor & ~(BLKSIZE - 1))
731 + BLKSIZE
732 + (int)(build - tmpblk.c) - 1;
733 break;
734
735 case ctrl('A'):
736 case ctrl('P'):
737 if (cursor < to)
738 {
739 delete(cursor, to);
740 }
741 if (*key == ctrl('A'))
742 {
743 cutname('.');
744 }
08746e8b
AM
745 m = paste(cursor, FALSE, TRUE);
746 if (m != MARK_UNSET)
747 {
748 to = cursor = m + 1L;
749 }
15637ed4
RG
750 break;
751
752 case ctrl('V'):
753 if (cursor >= to && when != WHEN_VIREP)
754 {
755 add(cursor, "^");
756 }
757 else
758 {
759 change(cursor, to, "^");
760 to = cursor + 1;
761 }
762 redraw(cursor, TRUE);
763 *key = getkey(0);
764 if (*key == '\n')
765 {
766 /* '\n' too hard to handle */
767#if OSK
768 *key = '\l';
769#else
770 *key = '\r';
771#endif
772 }
773 change(cursor, cursor + 1, key);
774 cursor++;
775 if (cursor > to)
776 {
777 to = cursor;
778 }
779 break;
780
781 case ctrl('L'):
782 case ctrl('R'):
783 redraw(MARK_UNSET, FALSE);
784 break;
785
786 default:
787 if (cursor >= to && when != WHEN_VIREP)
788 {
789 add(cursor, key);
790 cursor++;
791 to = cursor;
792 }
793 else
794 {
795 pfetch(markline(cursor));
796 if (markidx(cursor) == plen)
797 {
798 add(cursor, key);
799 }
800 else
801 {
802#ifndef NO_DIGRAPH
803 *key = digraph(ptext[markidx(cursor)], *key);
804#endif
805 change(cursor, cursor + 1, key);
806 }
807 cursor++;
808 }
809#ifndef NO_SHOWMATCH
810 /* show matching "({[" if necessary */
811 if (*o_showmatch && strchr(")}]", *key))
812 {
813 redraw(cursor, TRUE);
814 m = m_match(cursor - 1, 0L);
815 if (markline(m) >= topline
816 && markline(m) <= botline)
817 {
818 redraw(m, TRUE);
819 refresh();
820 sleep(1);
821 }
822 }
823#endif
824 } /* end switch(*key) */
825 } /* end for(;;) */
826BreakBreak:;
827 /* delete any excess characters */
828 if (cursor < to)
829 {
830#ifndef NO_EXTENSIONS
831 /* if we aren't in the middle of a change, start one! */
832 if (!inchg)
833 {
834 beforedo(FALSE);
835 inchg = TRUE;
836 }
837#endif
838 delete(cursor, to);
839 }
840
841 } /* end if doingdot else */
842
843 /* put the new text into a cut buffer for possible reuse */
844 if (!doingdot)
845 {
846 blksync();
847 cutname('.');
848 cut(from, cursor);
849 }
850
851 /* move to last char that we inputted, unless it was newline */
852 if (markidx(cursor) != 0)
853 {
854 cursor--;
855 }
856 redraw(cursor, FALSE);
857
858#ifndef NO_EXTENSIONS
859 if (quit)
860 {
861 /* if this is a nested "do", then cut it short */
862 abortdo();
863
864 /* exit, unless we can't write out the file */
865 cursor = v_xit(cursor, 0L, 'Z');
866 }
867#endif
868
869 rptlines = 0L;
870 return cursor;
871}