5 * 14407 SW Teal Blvd. #C
11 /* This file contains the input() function, which implements vi's INPUT mode.
12 * It also contains the code that supports digraphs.
30 char digraph(key1, key2)
31 char key1; /* the underlying character */
32 char key2; /* the second character */
37 /* if digraphs are disabled, then just return the new char */
43 /* remember the new key, so we can return it if this isn't a digraph */
46 /* sort key1 and key2, so that their original order won't matter */
53 /* scan through the digraph chart */
55 dp && (dp->key1 != key1 || dp->key2 != key2);
60 /* if this combination isn't in there, just use the new key */
66 /* else use the digraph key */
70 /* this function lists or defines digraphs */
71 void do_digraph(bang, extra)
78 static int user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
81 /* if "extra" is NULL, then we've reached the end of the built-ins */
88 /* if no args, then display the existing digraphs */
91 listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
93 for (dig = 0, dp = digs; dp; dp = dp->next)
104 listbuf[3] = dp->key1;
105 listbuf[4] = dp->key2;
106 listbuf[6] = dp->dig;
115 /* make sure we have at least two characters */
118 msg("Digraphs must be composed of two characters");
122 /* sort key1 and key2, so that their original order won't matter */
123 if (extra[0] > extra[1])
130 /* locate the new digraph character */
131 for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
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)
147 /* deleting the digraph? */
153 msg("%c%c not a digraph", extra[0], extra[1]);
158 prev->next = dp->next;
165 /* if necessary, create a new digraph struct for the new digraph */
168 dp = (struct _DIG *)malloc(sizeof *dp);
171 msg("Out of space in the digraph table");
178 dp->next = (struct _DIG *)0;
181 /* assign it the new digraph value */
185 dp->save = user_defined;
192 static char buf[] = "digraph! XX Y\n";
195 for (dp = digs; dp; dp = dp->next)
202 write(fd, buf, (unsigned)14);
214 char *large; /* the expanded form */
215 char small[1]; /* the abbreviated form (appended to struct) */
219 /* This functions lists or defines abbreviations */
223 int smlen; /* length of the small form */
224 int lrg; /* index of the start of the large form */
225 REG struct _AB *ab; /* used to move through the abbrev list */
231 /* list all current abbreviations */
232 for (ab = abbrev; ab; ab = ab->next)
244 /* else one or more arguments. Parse the first & look up in abbrev[] */
245 for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
248 for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
250 if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
256 /* locate the start of the large form, if any */
257 for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
264 /* trying to undo an abbreviation which doesn't exist? */
268 msg("\"%s\" not an abbreviation", extra);
273 /* undo the abbreviation */
275 prev->next = ab->next;
284 /* multiple args - [re]define an abbreviation */
287 /* redefining - free the old large form */
292 /* adding a new definition - make a new struct */
293 ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
297 msg("Out of memory -- Sorry");
301 strncpy(ab->small, extra, smlen);
302 ab->small[smlen] = '\0';
303 ab->next = (struct _AB *)0;
310 /* store the new form */
311 ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
312 strcpy(ab->large, &extra[lrg]);
317 /* This function is called from cmd_mkexrc() to save the abbreviations */
319 int fd; /* fd to which the :abbr commands should be written */
323 for (ab = abbrev; ab; ab = ab->next)
325 twrite(fd, "abbr ", 5);
326 twrite(fd, ab->small, strlen(ab->small));
328 twrite(fd, ab->large, strlen(ab->large));
334 /* This function should be called before each char is inserted. If the next
335 * char is non-alphanumeric and we're at the end of a word, then that word
336 * is checked against the abbrev[] array and expanded, if appropriate. Upon
337 * returning from this function, the new char still must be inserted.
339 static MARK expandabbr(m, ch)
340 MARK m; /* the cursor position */
341 int ch; /* the character to insert */
343 char *word; /* where the word starts */
344 int len; /* length of the word */
347 /* if no abbreviations are in effect, or ch is aphanumeric, then
350 if (!abbrev || !isascii(ch) || isalnum(ch))
355 /* see where the preceding word starts */
357 for (word = ptext + markidx(m), len = 0;
358 --word >= ptext && (!isascii(*word) || isalnum(*word));
364 /* if zero-length, then it isn't a word, really -- so nothing */
370 /* look it up in the abbrev list */
371 for (ab = abbrev; ab; ab = ab->next)
373 if (!strncmp(ab->small, word, len) && !ab->small[len])
379 /* not an abbreviation? then do nothing */
385 /* else replace the small form with the large form */
389 /* return with the cursor after the end of the large form */
390 return m - len + strlen(ab->large);
395 /* This function allows the user to replace an existing (possibly zero-length)
396 * chunk of text with typed-in text. It returns the MARK of the last character
397 * that the user typed in.
399 MARK input(from, to, when)
400 MARK from; /* where to start inserting text */
401 MARK to; /* extent of text to delete */
402 int when; /* either WHEN_VIINP or WHEN_VIREP */
404 char key[2]; /* key char followed by '\0' char */
405 char *build; /* used in building a newline+indent string */
406 char *scan; /* used while looking at the indent chars of a line */
407 MARK m; /* some place in the text */
408 #ifndef NO_EXTENSIONS
409 int quit = FALSE; /* boolean: are we exiting after this? */
413 /* if "from" and "to" are reversed, complain */
416 msg("ERROR: input(%ld:%d, %ld:%d)",
417 markline(from), markidx(from),
418 markline(to), markidx(to));
425 /* if we're replacing text with new text, save the old stuff */
426 /* (Alas, there is no easy way to save text for replace mode) */
434 /* if doing a dot command, then reuse the previous text */
437 /* delete the text that's there now */
443 /* insert the previous text */
445 cursor = paste(from, FALSE, TRUE) + 1L;
447 else /* interactive version */
449 /* if doing a change within the line... */
450 if (from != to && markline(from) == markline(to))
452 /* mark the end of the text with a "$" */
453 change(to - 1, to, "$");
457 /* delete the old text right off */
465 /* handle autoindent of the first line, maybe */
467 if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
469 /* Only autoindent blank lines. */
470 pfetch(markline(cursor));
473 /* Okay, we really want to autoindent */
474 pfetch(markline(cursor) - 1L);
475 for (scan = ptext, build = tmpblk.c;
476 *scan == ' ' || *scan == '\t';
481 if (build > tmpblk.c)
484 add(cursor, tmpblk.c);
485 cursor += (build - tmpblk.c);
490 /* repeatedly add characters from the user */
493 /* Get a character */
494 redraw(cursor, TRUE);
496 msg("cursor=%ld.%d, to=%ld.%d",
497 markline(cursor), markidx(cursor),
498 markline(to), markidx(to));
500 key[0] = getkey(when);
502 /* if whitespace & wrapmargin is set & we're
503 * past the warpmargin, then change the
504 * whitespace character into a newline
506 if ((*key == ' ' || *key == '\t')
507 && *o_wrapmargin != 0)
509 pfetch(markline(cursor));
510 if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
519 #ifndef NO_EXTENSIONS
520 case 0: /* special movement mapped keys */
524 case 'h': m = m_left(cursor, 0L); break;
526 case 'k': m = m_updnto(cursor, 0L, *key); break;
527 case 'l': m = cursor + 1; break;
528 case 'b': m = m_bword(cursor, 0L); break;
529 case 'w': m = m_fword(cursor, 0L); break;
530 case '^': m = m_front(cursor, 0L); break;
531 case '$': m = m_rear(cursor, 0L); break;
534 m = m_scroll(cursor, 0L, *key); break;
535 case 'x': m = v_xchar(cursor, 0L); break;
536 case 'i': m = to = from = cursor; break;
537 default: m = MARK_UNSET; break;
539 /* adjust the moved cursor */
540 m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
541 if (*key == '$' || (*key == 'l' && m <= cursor))
545 /* if the cursor is reasonable, use it */
555 redraw(cursor, TRUE);
557 from = to = cursor = m;
562 if (getkey(0) == ctrl('Z'))
572 cursor = expandabbr(cursor, ctrl('['));
577 if (markline(cursor) == markline(from))
583 cursor &= ~(BLKSIZE - 1);
594 cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
601 cursor = m_front(cursor, 0L);
611 else if (markidx(cursor) == 0)
614 pfetch(markline(cursor));
624 m = m_bword(cursor, 1L);
625 if (markline(m) == markline(cursor) && m >= from)
646 cursor = expandabbr(cursor, '\n');
652 /* figure out indent for next line */
653 pfetch(markline(cursor));
654 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
659 /* remove indent from this line, if blank */
660 if (!*scan && plen > 0)
662 to = cursor &= ~(BLKSIZE - 1);
663 delete(cursor, cursor + plen);
667 if (cursor >= to && when != WHEN_VIREP)
669 add(cursor, tmpblk.c);
673 change(cursor, to, tmpblk.c);
675 redraw(cursor, TRUE);
676 to = cursor = (cursor & ~(BLKSIZE - 1))
678 + (int)(build - tmpblk.c) - 1;
687 if (*key == ctrl('A'))
691 to = cursor = paste(cursor, FALSE, TRUE) + 1L;
695 if (cursor >= to && when != WHEN_VIREP)
701 change(cursor, to, "^");
704 redraw(cursor, TRUE);
708 /* '\n' too hard to handle */
715 change(cursor, cursor + 1, key);
725 redraw(MARK_UNSET, FALSE);
729 if (cursor >= to && when != WHEN_VIREP)
732 cursor = expandabbr(cursor, *key);
740 pfetch(markline(cursor));
741 if (markidx(cursor) == plen)
744 cursor = expandabbr(cursor, *key);
751 *key = digraph(ptext[markidx(cursor)], *key);
754 cursor = expandabbr(cursor, *key);
756 change(cursor, cursor + 1, key);
761 /* show matching "({[" if neceesary */
762 if (*o_showmatch && strchr(")}]", *key))
764 redraw(cursor, TRUE);
765 m = m_match(cursor - 1, 0L);
766 if (markline(m) >= topline
767 && markline(m) <= botline)
775 } /* end switch(*key) */
779 /* delete any excess characters */
785 } /* end if doingdot else */
787 } /* end ChangeText */
789 /* put the new text into a cut buffer for possible reuse */
797 /* move to last char that we inputted, unless it was newline */
798 if (markidx(cursor) != 0)
802 redraw(cursor, FALSE);
804 #ifndef NO_EXTENSIONS
807 /* if this is a nested "do", then cut it short */
810 /* exit, unless we can't write out the file */
811 cursor = v_xit(cursor, 0L, 'Z');