|
|
1.1 root 1: /* input.c */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 16820 SW Tallac Way
6: * Beaverton, OR 97006
7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda
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 <ctype.h>
16: #include "config.h"
17: #include "vi.h"
18:
19:
20: #ifndef NO_DIGRAPH
21: static struct _DIG
22: {
23: struct _DIG *next;
24: char key1;
25: char key2;
26: char dig;
27: char save;
28: } *digs;
29:
30: char 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 */
71: void 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:
80: /* if "extra" is NULL, then we've reached the end of the built-ins */
81: if (!extra)
82: {
83: user_defined = TRUE;
84: return;
85: }
86:
87: /* if no args, then display the existing digraphs */
88: if (*extra < ' ')
89: {
90: for (dp = digs; dp; dp = dp->next)
91: {
92: if (dp->save || bang)
93: {
94: addstr("digraph ");
95: addch(dp->key1);
96: addch(dp->key2);
97: addch(' ');
98: addch(dp->dig);
99: addch('\n');
100: exrefresh();
101: }
102: }
103: return;
104: }
105:
106: /* make sure we have at least two characters */
107: if (!extra[1])
108: {
109: msg("Digraphs must be composed of two characters");
110: return;
111: }
112:
113: /* sort key1 and key2, so that their original order won't matter */
114: if (extra[0] > extra[1])
115: {
116: dig = extra[0];
117: extra[0] = extra[1];
118: extra[1] = dig;
119: }
120:
121: /* locate the new digraph character */
122: for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
123: {
124: }
125: dig = extra[dig];
126: if (!bang && dig)
127: {
128: dig |= 0x80;
129: }
130:
131: /* search for the digraph */
132: for (prev = (struct _DIG *)0, dp = digs;
133: dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
134: prev = dp, dp = dp->next)
135: {
136: }
137:
138: /* deleting the digraph? */
139: if (!dig)
140: {
141: if (!dp)
142: {
143: #ifndef CRUNCH
144: msg("%c%c not a digraph", extra[0], extra[1]);
145: #endif
146: return;
147: }
148: if (prev)
149: prev->next = dp->next;
150: else
151: digs = dp->next;
152: free(dp);
153: return;
154: }
155:
156: /* if necessary, create a new digraph struct for the new digraph */
157: if (dig && !dp)
158: {
159: dp = (struct _DIG *)malloc(sizeof *dp);
160: if (!dp)
161: {
162: msg("Out of space in the digraph table");
163: return;
164: }
165: if (prev)
166: prev->next = dp;
167: else
168: digs = dp;
169: dp->next = (struct _DIG *)0;
170: }
171:
172: /* assign it the new digraph value */
173: dp->key1 = extra[0];
174: dp->key2 = extra[1];
175: dp->dig = dig;
176: dp->save = user_defined;
177: }
178:
179: # ifndef NO_MKEXRC
180: void savedigs(fd)
181: int fd;
182: {
183: static char buf[] = "digraph! XX Y\n";
184: REG struct _DIG *dp;
185:
186: for (dp = digs; dp; dp = dp->next)
187: {
188: if (dp->save)
189: {
190: buf[9] = dp->key1;
191: buf[10] = dp->key2;
192: buf[12] = dp->dig;
193: write(fd, buf, (unsigned)14);
194: }
195: }
196: }
197: # endif
198: #endif
199:
200:
201: #ifndef NO_ABBR
202: static struct _AB
203: {
204: struct _AB *next;
205: char *large; /* the expanded form */
206: char small[1]; /* the abbreviated form (appended to struct) */
207: }
208: *abbrev;
209:
210: /* This functions lists or defines abbreviations */
211: void do_abbr(extra)
212: char *extra;
213: {
214: int smlen; /* length of the small form */
215: int lrg; /* index of the start of the large form */
216: REG struct _AB *ab; /* used to move through the abbrev list */
217: struct _AB *prev;
218:
219: /* no arguments? */
220: if (!*extra)
221: {
222: /* list all current abbreviations */
223: for (ab = abbrev; ab; ab = ab->next)
224: {
225: qaddstr("abbr ");
226: qaddstr(ab->small);
227: qaddch(' ');
228: qaddstr(ab->large);
229: addch('\n');
230: exrefresh();
231: }
232: return;
233: }
234:
235: /* else one or more arguments. Parse the first & look up in abbrev[] */
236: for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
237: {
238: }
239: for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
240: {
241: if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
242: {
243: break;
244: }
245: }
246:
247: /* locate the start of the large form, if any */
248: for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
249: {
250: }
251:
252: /* only one arg? */
253: if (!extra[lrg])
254: {
255: /* trying to undo an abbreviation which doesn't exist? */
256: if (!ab)
257: {
258: #ifndef CRUNCH
259: msg("\"%s\" not an abbreviation", extra);
260: #endif
261: return;
262: }
263:
264: /* undo the abbreviation */
265: if (prev)
266: prev->next = ab->next;
267: else
268: abbrev = ab->next;
269: free(ab->large);
270: free(ab);
271:
272: return;
273: }
274:
275: /* multiple args - [re]define an abbreviation */
276: if (ab)
277: {
278: /* redefining - free the old large form */
279: free(ab->large);
280: }
281: else
282: {
283: /* adding a new definition - make a new struct */
284: ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
285: #ifndef CRUNCH
286: if (!ab)
287: {
288: msg("Out of memory -- Sorry");
289: return;
290: }
291: #endif
292: strncpy(ab->small, extra, smlen);
293: ab->small[smlen] = '\0';
294: ab->next = (struct _AB *)0;
295: if (prev)
296: prev->next = ab;
297: else
298: abbrev = ab;
299: }
300:
301: /* store the new form */
302: ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
303: strcpy(ab->large, &extra[lrg]);
304: }
305:
306:
307: # ifndef NO_MKEXRC
308: /* This function is called from cmd_mkexrc() to save the abbreviations */
309: void saveabbr(fd)
310: int fd; /* fd to which the :abbr commands should be written */
311: {
312: REG struct _AB *ab;
313:
314: for (ab = abbrev; ab; ab = ab->next)
315: {
316: twrite(fd, "abbr ", 5);
317: twrite(fd, ab->small, strlen(ab->small));
318: twrite(fd, " ", 1);
319: twrite(fd, ab->large, strlen(ab->large));
320: twrite(fd, "\n", 1);
321: }
322: }
323: # endif
324:
325: /* This function should be called before each char is inserted. If the next
326: * char is non-alphanumeric and we're at the end of a word, then that word
327: * is checked against the abbrev[] array and expanded, if appropriate. Upon
328: * returning from this function, the new char still must be inserted.
329: */
330: static MARK expandabbr(m, ch)
331: MARK m; /* the cursor position */
332: int ch; /* the character to insert */
333: {
334: char *word; /* where the word starts */
335: int len; /* length of the word */
336: REG struct _AB *ab;
337:
338: /* if no abbreviations are in effect, or ch is aphanumeric, then
339: * don't do anything
340: */
341: if (!abbrev || !isascii(ch) || isalnum(ch))
342: {
343: return m;
344: }
345:
346: /* see where the preceding word starts */
347: pfetch(markline(m));
348: for (word = ptext + markidx(m), len = 0;
349: --word >= ptext && (!isascii(*word) || isalnum(*word));
350: len++)
351: {
352: }
353: word++;
354:
355: /* if zero-length, then it isn't a word, really -- so nothing */
356: if (len == 0)
357: {
358: return m;
359: }
360:
361: /* look it up in the abbrev list */
362: for (ab = abbrev; ab; ab = ab->next)
363: {
364: if (!strncmp(ab->small, word, len) && !ab->small[len])
365: {
366: break;
367: }
368: }
369:
370: /* not an abbreviation? then do nothing */
371: if (!ab)
372: {
373: return m;
374: }
375:
376: /* else replace the small form with the large form */
377: add(m, ab->large);
378: delete(m - len, m);
379:
380: /* return with the cursor after the end of the large form */
381: return m - len + strlen(ab->large);
382: }
383: #endif
384:
385:
386: /* This function allows the user to replace an existing (possibly zero-length)
387: * chunk of text with typed-in text. It returns the MARK of the last character
388: * that the user typed in.
389: */
390: MARK input(from, to, when)
391: MARK from; /* where to start inserting text */
392: MARK to; /* extent of text to delete */
393: int when; /* either WHEN_VIINP or WHEN_VIREP */
394: {
395: char key[2]; /* key char followed by '\0' char */
396: char *build; /* used in building a newline+indent string */
397: char *scan; /* used while looking at the indent chars of a line */
398: MARK m; /* some place in the text */
399: #ifndef NO_EXTENSIONS
400: int quit = FALSE; /* boolean: are we exiting after this? */
401: #endif
402:
403: #ifdef DEBUG
404: /* if "from" and "to" are reversed, complain */
405: if (from > to)
406: {
407: msg("ERROR: input(%ld:%d, %ld:%d)",
408: markline(from), markidx(from),
409: markline(to), markidx(to));
410: return MARK_UNSET;
411: }
412: #endif
413:
414: key[1] = 0;
415:
416: /* if we're replacing text with new text, save the old stuff */
417: /* (Alas, there is no easy way to save text for replace mode) */
418: if (from != to)
419: {
420: cut(from, to);
421: }
422:
423: ChangeText
424: {
425: /* if doing a dot command, then reuse the previous text */
426: if (doingdot)
427: {
428: /* delete the text that's there now */
429: if (from != to)
430: {
431: delete(from, to);
432: }
433:
434: /* insert the previous text */
435: cutname('.');
436: cursor = paste(from, FALSE, TRUE) + 1L;
437: }
438: else /* interactive version */
439: {
440: /* if doing a change within the line... */
441: if (from != to && markline(from) == markline(to))
442: {
443: /* mark the end of the text with a "$" */
444: change(to - 1, to, "$");
445: }
446: else
447: {
448: /* delete the old text right off */
449: if (from != to)
450: {
451: delete(from, to);
452: }
453: to = from;
454: }
455:
456: /* handle autoindent of the first line, maybe */
457: cursor = from;
458: if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
459: {
460: /* Only autoindent blank lines. */
461: pfetch(markline(cursor));
462: if (plen == 0)
463: {
464: /* Okay, we really want to autoindent */
465: pfetch(markline(cursor) - 1L);
466: for (scan = ptext, build = tmpblk.c;
467: *scan == ' ' || *scan == '\t';
468: )
469: {
470: *build++ = *scan++;
471: }
472: if (build > tmpblk.c)
473: {
474: *build = '\0';
475: add(cursor, tmpblk.c);
476: cursor += (build - tmpblk.c);
477: }
478: }
479: }
480:
481: /* repeatedly add characters from the user */
482: for (;;)
483: {
484: /* Get a character */
485: redraw(cursor, TRUE);
486: #ifdef DEBUG
487: msg("cursor=%ld.%d, to=%ld.%d",
488: markline(cursor), markidx(cursor),
489: markline(to), markidx(to));
490: #endif
491: key[0] = getkey(when);
492:
493: /* if whitespace & wrapmargin is set & we're
494: * past the warpmargin, then change the
495: * whitespace character into a newline
496: */
497: if ((*key == ' ' || *key == '\t')
498: && *o_wrapmargin != 0)
499: {
500: pfetch(markline(cursor));
501: if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
502: {
503: *key = '\n';
504: }
505: }
506:
507: /* process it */
508: switch (*key)
509: {
510: #ifndef NO_EXTENSIONS
511: case 0: /* special movement mapped keys */
512: *key = getkey(0);
513: switch (*key)
514: {
515: case 'h': m = m_left(cursor, 0L); break;
516: case 'j':
517: case 'k': m = m_updnto(cursor, 0L, *key); break;
518: case 'l': m = cursor + 1; break;
519: case 'b': m = m_bword(cursor, 0L); break;
520: case 'w': m = m_fword(cursor, 0L); break;
521: case '^': m = m_front(cursor, 0L); break;
522: case '$': m = m_rear(cursor, 0L); break;
523: case ctrl('B'):
524: case ctrl('F'):
525: m = m_scroll(cursor, 0L, *key); break;
526: case 'x': m = v_xchar(cursor, 0L); break;
527: case 'i': m = to = from = cursor; break;
528: default: m = MARK_UNSET; break;
529: }
530: /* adjust the moved cursor */
531: m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
532: if (*key == '$' || (*key == 'l' && m <= cursor))
533: {
534: m++;
535: }
536: /* if the cursor is reasonable, use it */
537: if (m == MARK_UNSET)
538: {
539: beep();
540: }
541: else
542: {
543: if (to > cursor)
544: {
545: delete(cursor, to);
546: redraw(cursor, TRUE);
547: }
548: from = to = cursor = m;
549: }
550: break;
551:
552: case ctrl('Z'):
553: if (getkey(0) == ctrl('Z'))
554: {
555: quit = TRUE;
556: goto BreakBreak;
557: }
558: break;
559: #endif
560:
561: case ctrl('['):
562: #ifndef NO_ABBR
563: cursor = expandabbr(cursor, ctrl('['));
564: #endif
565: goto BreakBreak;
566:
567: case ctrl('U'):
568: if (markline(cursor) == markline(from))
569: {
570: cursor = from;
571: }
572: else
573: {
574: cursor &= ~(BLKSIZE - 1);
575: }
576: break;
577:
578: case ctrl('D'):
579: case ctrl('T'):
580: if (to > cursor)
581: {
582: delete(cursor, to);
583: }
584: mark[27] = cursor;
585: cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
586: if (mark[27])
587: {
588: cursor = mark[27];
589: }
590: else
591: {
592: cursor = m_front(cursor, 0L);
593: }
594: to = cursor;
595: break;
596:
597: case '\b':
598: if (cursor <= from)
599: {
600: beep();
601: }
602: else if (markidx(cursor) == 0)
603: {
604: cursor -= BLKSIZE;
605: pfetch(markline(cursor));
606: cursor += plen;
607: }
608: else
609: {
610: cursor--;
611: }
612: break;
613:
614: case ctrl('W'):
615: m = m_bword(cursor, 1L);
616: if (markline(m) == markline(cursor) && m >= from)
617: {
618: cursor = m;
619: if (from > cursor)
620: {
621: from = cursor;
622: }
623: }
624: else
625: {
626: beep();
627: }
628: break;
629:
630: case '\n':
631: #if OSK
632: case '\l':
633: #else
634: case '\r':
635: #endif
636: #ifndef NO_ABBR
637: cursor = expandabbr(cursor, '\n');
638: #endif
639: build = tmpblk.c;
640: *build++ = '\n';
641: if (*o_autoindent)
642: {
643: /* figure out indent for next line */
644: pfetch(markline(cursor));
645: for (scan = ptext; *scan == ' ' || *scan == '\t'; )
646: {
647: *build++ = *scan++;
648: }
649:
650: /* remove indent from this line, if blank */
651: if (!*scan && plen > 0)
652: {
653: to = cursor &= ~(BLKSIZE - 1);
654: delete(cursor, cursor + plen);
655: }
656: }
657: *build = 0;
658: if (cursor >= to && when != WHEN_VIREP)
659: {
660: add(cursor, tmpblk.c);
661: }
662: else
663: {
664: change(cursor, to, tmpblk.c);
665: }
666: redraw(cursor, TRUE);
667: to = cursor = (cursor & ~(BLKSIZE - 1))
668: + BLKSIZE
669: + (int)(build - tmpblk.c) - 1;
670: break;
671:
672: case ctrl('A'):
673: case ctrl('P'):
674: if (cursor < to)
675: {
676: delete(cursor, to);
677: }
678: if (*key == ctrl('A'))
679: {
680: cutname('.');
681: }
682: to = cursor = paste(cursor, FALSE, TRUE) + 1L;
683: break;
684:
685: case ctrl('V'):
686: if (cursor >= to && when != WHEN_VIREP)
687: {
688: add(cursor, "^");
689: }
690: else
691: {
692: change(cursor, to, "^");
693: to = cursor + 1;
694: }
695: redraw(cursor, TRUE);
696: *key = getkey(0);
697: if (*key == '\n')
698: {
699: /* '\n' too hard to handle */
700: #if OSK
701: *key = '\l';
702: #else
703: *key = '\r';
704: #endif
705: }
706: change(cursor, cursor + 1, key);
707: cursor++;
708: if (cursor > to)
709: {
710: to = cursor;
711: }
712: break;
713:
714: case ctrl('L'):
715: case ctrl('R'):
716: redraw(MARK_UNSET, FALSE);
717: break;
718:
719: default:
720: if (cursor >= to && when != WHEN_VIREP)
721: {
722: #ifndef NO_ABBR
723: cursor = expandabbr(cursor, *key);
724: #endif
725: add(cursor, key);
726: cursor++;
727: to = cursor;
728: }
729: else
730: {
731: pfetch(markline(cursor));
732: if (markidx(cursor) == plen)
733: {
734: #ifndef NO_ABBR
735: cursor = expandabbr(cursor, *key);
736: #endif
737: add(cursor, key);
738: }
739: else
740: {
741: #ifndef NO_DIGRAPH
742: *key = digraph(ptext[markidx(cursor)], *key);
743: #endif
744: #ifndef NO_ABBR
745: cursor = expandabbr(cursor, *key);
746: #endif
747: change(cursor, cursor + 1, key);
748: }
749: cursor++;
750: }
751: #ifndef NO_SHOWMATCH
752: /* show matching "({[" if neceesary */
753: if (*o_showmatch && strchr(")}]", *key))
754: {
755: m = m_match(cursor - 1, 0L);
756: if (markline(m) >= topline
757: && markline(m) <= botline)
758: {
759: redraw(m, TRUE);
760: refresh();
761: sleep(1);
762: }
763: }
764: #endif
765: } /* end switch(*key) */
766: } /* end for(;;) */
767: BreakBreak:;
768:
769: /* delete any excess characters */
770: if (cursor < to)
771: {
772: delete(cursor, to);
773: }
774:
775: } /* end if doingdot else */
776:
777: } /* end ChangeText */
778:
779: /* put the new text into a cut buffer for possible reuse */
780: if (!doingdot)
781: {
782: blksync();
783: cutname('.');
784: cut(from, cursor);
785: }
786:
787: /* move to last char that we inputted, unless it was newline */
788: if (markidx(cursor) != 0)
789: {
790: cursor--;
791: }
792: redraw(cursor, FALSE);
793:
794: #ifndef NO_EXTENSIONS
795: if (quit)
796: {
797: refresh();
798: cursor = v_xit(cursor, 0L, 'Z');
799: }
800: #endif
801:
802: rptlines = 0L;
803: return cursor;
804: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.