source: trunk/minix/commands/elvis/input.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.1 KB
Line 
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)
31 char key1; /* the underlying character */
32 char key2; /* the second character */
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;
161 free(dp);
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 */
214MARK input(from, to, when, above)
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 */
218 int above; /* boolean: take indentation from lower line? */
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;
290 m = (above ? (cursor + BLKSIZE) : (cursor - BLKSIZE));
291 if (*o_autoindent && markidx(m) == 0
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);
310 cursor += (build - tmpblk.c);
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);
332 for (scan = ptext + markidx(cursor); --scan >= build && isalnum(*scan); )
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
341 if (key[0] != '\0' && V_from != MARK_UNSET)
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 */
378 if (*o_wrapmargin != 0)
379 {
380 pfetch(markline(cursor));
381 if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
382 {
383 build = tmpblk.c;
384 *build++ = '\n';
385 if (*o_autoindent)
386 {
387 /* figure out indent for next line */
388 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
389 {
390 *build++ = *scan++;
391 }
392 }
393 *build = '\0';
394
395 scan = ptext + plen;
396 m = cursor & ~(BLKSIZE - 1);
397 while (ptext < scan)
398 {
399 scan--;
400 if (*scan != ' ' && *scan != '\t')
401 continue;
402
403 /*break up line, and we do autoindent if needed*/
404 change(m + (scan - ptext), m + (scan - ptext) + 1, tmpblk.c);
405 cursor = (cursor & ~(BLKSIZE - 1))
406 + BLKSIZE
407 + strlen(tmpblk.c) - 1
408 + plen - (scan - ptext) - 1;
409
410 /*remove trailing spaces on previous line*/
411 pfetch(markline(m));
412 scan = ptext + plen;
413 while (ptext < scan)
414 {
415 scan--;
416 if (*scan != ' ' && *scan != '\t')
417 break;
418 }
419 delete(m + (scan-ptext) + 1, m + plen);
420
421 break;
422 }
423 }
424 }
425#endif /* !CRUNCH */
426
427 /* process it */
428 switch (*key)
429 {
430#ifndef NO_EXTENSIONS
431 case ctrl('O'): /* special movement mapped keys */
432 *key = getkey(0);
433 switch (*key)
434 {
435 case 'h': m = m_left(cursor, 0L); break;
436 case 'j':
437 case 'k': m = m_updnto(cursor, 0L, *key); break;
438 case 'l': m = cursor + 1; break;
439 case 'B':
440 case 'b': m = m_bword(cursor, 0L, *key); break;
441 case 'W':
442 case 'w': m = m_fword(cursor, 0L, *key, '\0'); break;
443 case '^': m = m_front(cursor, 0L); break;
444 case '$': m = m_rear(cursor, 0L); break;
445 case ctrl('B'):
446 case ctrl('F'):
447 m = m_scroll(cursor, 0L, *key); break;
448 case 'x':
449#ifndef NO_VISIBLE
450 if (V_from)
451 beep();
452 else
453#endif
454 ChangeText
455 {
456 m = v_xchar(cursor, 0L, 'x');
457 }
458 break;
459 case 'i': m = to = from = cursor;
460 when = WHEN_VIINP + WHEN_VIREP - when;
461 break;
462 case 'K':
463 pfetch(markline(cursor));
464 changes++; /* <- after this, we can alter ptext */
465 ptext[markidx(cursor)] = 0;
466 for (scan = ptext + markidx(cursor) - 1;
467 scan >= ptext && isalnum(*scan);
468 scan--)
469 {
470 }
471 scan++;
472 m = (*scan ? v_keyword(scan, cursor, 0L) : cursor);
473 break;
474
475# ifndef NO_VISIBLE
476 case 'v':
477 case 'V':
478 if (V_from)
479 V_from = MARK_UNSET;
480 else
481 V_from = cursor;
482 m = from = to = cursor;
483 V_linemd = (*key == 'V');
484 break;
485
486 case 'd':
487 case 'y':
488 case '\\':
489 /* do nothing if unmarked */
490 if (!V_from)
491 {
492 msg("You must mark the text first");
493 beep();
494 break;
495 }
496
497 /* "from" must come before "to" */
498 if (V_from < cursor)
499 {
500 from = V_from;
501 to = cursor;
502 }
503 else
504 {
505 from = cursor;
506 to = V_from;
507 }
508
509 /* we don't need V_from anymore */
510 V_from = MARK_UNSET;
511
512 if (V_linemd)
513 {
514 /* adjust for line mode */
515 from &= ~(BLKSIZE - 1);
516 to |= (BLKSIZE - 1);
517 }
518 else
519 {
520 /* in character mode, we must
521 * worry about deleting the newline
522 * at the end of the last line
523 */
524 pfetch(markline(to));
525 if (markidx(to) == plen)
526 to |= (BLKSIZE - 1);
527 }
528 to++;
529
530 switch (*key)
531 {
532 case 'y':
533 cut(from, to);
534 break;
535
536 case 'd':
537 ChangeText
538 {
539 cut(from, to);
540 delete(from, to);
541 }
542 cursor = from;
543 break;
544
545#ifndef NO_POPUP
546 case '\\':
547 ChangeText
548 {
549 cursor = v_popup(from, to);
550 }
551 break;
552#endif
553 }
554 m = from = to = cursor;
555 break;
556
557 case 'p':
558 case 'P':
559 V_from = MARK_UNSET;
560 ChangeText
561 {
562 m = from = to = cursor = paste(cursor, (*key == 'p'), FALSE);
563 }
564 break;
565# endif /* !NO_VISIBLE */
566 default: m = MARK_UNSET;
567 }
568
569 /* adjust the moved cursor */
570 if (m != cursor)
571 {
572 m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
573 if (*key == '$' || (*key == 'l' && m <= cursor))
574 {
575 m++;
576 }
577 }
578
579 /* if the cursor is reasonable, use it */
580 if (m == MARK_UNSET)
581 {
582 beep();
583 }
584 else
585 {
586 from = to = cursor = m;
587 }
588 break;
589
590 case ctrl('Z'):
591 if (getkey(0) == ctrl('Z'))
592 {
593 quit = TRUE;
594 goto BreakBreak;
595 }
596 break;
597#endif
598
599 case ctrl('['):
600 /* if last line contains only whitespace, then remove whitespace */
601 if (*o_autoindent)
602 {
603 pfetch(markline(cursor));
604 for (scan = ptext; isspace(*scan); scan++)
605 {
606 }
607 if (scan > ptext && !*scan)
608 {
609 cursor &= ~(BLKSIZE - 1L);
610 if (to < cursor + plen)
611 {
612 to = cursor + plen;
613 }
614 }
615 }
616 goto BreakBreak;
617
618 case ctrl('U'):
619 if (markline(cursor) == markline(from))
620 {
621 cursor = from;
622 }
623 else
624 {
625 cursor &= ~(BLKSIZE - 1);
626 }
627 break;
628
629 case ctrl('D'):
630 case ctrl('T'):
631 if (to > cursor)
632 {
633 delete(cursor, to);
634 }
635 mark[27] = cursor;
636 cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
637 if (mark[27])
638 {
639 cursor = mark[27];
640 }
641 else
642 {
643 cursor = m_front(cursor, 0L);
644 }
645 to = cursor;
646 break;
647
648 case '\b':
649 if (cursor <= from)
650 {
651 beep();
652 }
653 else if (markidx(cursor) == 0)
654 {
655 cursor -= BLKSIZE;
656 pfetch(markline(cursor));
657 cursor += plen;
658 }
659 else
660 {
661 cursor--;
662 }
663 break;
664
665 case ctrl('W'):
666 m = m_bword(cursor, 1L, 'b');
667 if (markline(m) == markline(cursor) && m >= from)
668 {
669 cursor = m;
670 if (from > cursor)
671 {
672 from = cursor;
673 }
674 }
675 else
676 {
677 beep();
678 }
679 break;
680
681 case '\n':
682#if OSK
683 case '\l':
684#else
685 case '\r':
686#endif
687 build = tmpblk.c;
688 *build++ = '\n';
689 if (*o_autoindent)
690 {
691 /* figure out indent for next line */
692 pfetch(markline(cursor));
693 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
694 {
695 *build++ = *scan++;
696 }
697
698 /* remove indent from this line, if blank */
699 if ((scan - ptext) >= markidx(cursor) && plen > 0)
700 {
701 to = cursor &= ~(BLKSIZE - 1);
702 delete(cursor, cursor + plen);
703 }
704 }
705 *build = 0;
706 if (cursor >= to && when != WHEN_VIREP)
707 {
708 add(cursor, tmpblk.c);
709 }
710 else
711 {
712 change(cursor, to, tmpblk.c);
713 }
714 redraw(cursor, TRUE);
715 to = cursor = (cursor & ~(BLKSIZE - 1))
716 + BLKSIZE
717 + (int)(build - tmpblk.c) - 1;
718 break;
719
720 case ctrl('A'):
721 case ctrl('P'):
722 if (cursor < to)
723 {
724 delete(cursor, to);
725 }
726 if (*key == ctrl('A'))
727 {
728 cutname('.');
729 }
730 to = cursor = paste(cursor, FALSE, TRUE) + 1L;
731 break;
732
733 case ctrl('V'):
734 if (cursor >= to && when != WHEN_VIREP)
735 {
736 add(cursor, "^");
737 }
738 else
739 {
740 change(cursor, to, "^");
741 to = cursor + 1;
742 }
743 redraw(cursor, TRUE);
744 *key = getkey(0);
745 if (*key == '\n')
746 {
747 /* '\n' too hard to handle */
748#if OSK
749 *key = '\l';
750#else
751 *key = '\r';
752#endif
753 }
754 change(cursor, cursor + 1, key);
755 cursor++;
756 if (cursor > to)
757 {
758 to = cursor;
759 }
760 break;
761
762 case ctrl('L'):
763 case ctrl('R'):
764 redraw(MARK_UNSET, FALSE);
765 break;
766
767 default:
768 if (cursor >= to && when != WHEN_VIREP)
769 {
770 add(cursor, key);
771 cursor++;
772 to = cursor;
773 }
774 else
775 {
776 pfetch(markline(cursor));
777 if (markidx(cursor) == plen)
778 {
779 add(cursor, key);
780 }
781 else
782 {
783#ifndef NO_DIGRAPH
784 *key = digraph(ptext[markidx(cursor)], *key);
785#endif
786 change(cursor, cursor + 1, key);
787 }
788 cursor++;
789 }
790#ifndef NO_SHOWMATCH
791 /* show matching "({[" if necessary */
792 if (*o_showmatch && strchr(")}]", *key))
793 {
794 redraw(cursor, TRUE);
795 m = m_match(cursor - 1, 0L);
796 if (markline(m) >= topline
797 && markline(m) <= botline)
798 {
799 redraw(m, TRUE);
800 refresh();
801 sleep(1);
802 }
803 }
804#endif
805 } /* end switch(*key) */
806 } /* end for(;;) */
807BreakBreak:;
808 /* delete any excess characters */
809 if (cursor < to)
810 {
811#ifndef NO_EXTENSIONS
812 /* if we aren't in the middle of a change, start one! */
813 if (!inchg)
814 {
815 beforedo(FALSE);
816 inchg = TRUE;
817 }
818#endif
819 delete(cursor, to);
820 }
821
822 } /* end if doingdot else */
823
824 /* put the new text into a cut buffer for possible reuse */
825 if (!doingdot)
826 {
827 blksync();
828 cutname('.');
829 cut(from, cursor);
830 }
831
832 /* move to last char that we inputted, unless it was newline */
833 if (markidx(cursor) != 0)
834 {
835 cursor--;
836 }
837 redraw(cursor, FALSE);
838
839#ifndef NO_EXTENSIONS
840 if (quit)
841 {
842 /* if this is a nested "do", then cut it short */
843 abortdo();
844
845 /* exit, unless we can't write out the file */
846 cursor = v_xit(cursor, 0L, 'Z');
847 }
848#endif
849
850 rptlines = 0L;
851 return cursor;
852}
Note: See TracBrowser for help on using the repository browser.