unstack, sort: cleanup and improvement
[minix.git] / commands / elvis / input.c
blob563f64351aba7f654dd85935e6179dff245f6540
1 /* input.c */
3 /* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
8 */
11 /* This file contains the input() function, which implements vi's INPUT mode.
12 * It also contains the code that supports digraphs.
15 #include "config.h"
16 #include "ctype.h"
17 #include "vi.h"
20 #ifndef NO_DIGRAPH
21 static struct _DIG
23 struct _DIG *next;
24 char key1;
25 char key2;
26 char dig;
27 char save;
28 } *digs;
30 char digraph(key1, key2)
31 char key1; /* the underlying character */
32 char key2; /* the second character */
34 int newkey;
35 REG struct _DIG *dp;
37 /* if digraphs are disabled, then just return the new char */
38 if (!*o_digraph)
40 return key2;
43 /* remember the new key, so we can return it if this isn't a digraph */
44 newkey = key2;
46 /* sort key1 and key2, so that their original order won't matter */
47 if (key1 > key2)
49 key2 = key1;
50 key1 = newkey;
53 /* scan through the digraph chart */
54 for (dp = digs;
55 dp && (dp->key1 != key1 || dp->key2 != key2);
56 dp = dp->next)
60 /* if this combination isn't in there, just use the new key */
61 if (!dp)
63 return newkey;
66 /* else use the digraph key */
67 return dp->dig;
70 /* this function lists or defines digraphs */
71 void do_digraph(bang, extra)
72 int bang;
73 char extra[];
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];
81 /* if "extra" is NULL, then we've reached the end of the built-ins */
82 if (!extra)
84 user_defined = TRUE;
85 return;
88 /* if no args, then display the existing digraphs */
89 if (*extra < ' ')
91 listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
92 listbuf[7] = '\0';
93 for (dig = 0, dp = digs; dp; dp = dp->next)
95 if (dp->save || bang)
97 dig += 7;
98 if (dig >= COLS)
100 addch('\n');
101 exrefresh();
102 dig = 7;
104 listbuf[3] = dp->key1;
105 listbuf[4] = dp->key2;
106 listbuf[6] = dp->dig;
107 qaddstr(listbuf);
110 addch('\n');
111 exrefresh();
112 return;
115 /* make sure we have at least two characters */
116 if (!extra[1])
118 msg("Digraphs must be composed of two characters");
119 return;
122 /* sort key1 and key2, so that their original order won't matter */
123 if (extra[0] > extra[1])
125 dig = extra[0];
126 extra[0] = extra[1];
127 extra[1] = dig;
130 /* locate the new digraph character */
131 for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
134 dig = extra[dig];
135 if (!bang && dig)
137 dig |= 0x80;
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? */
148 if (!dig)
150 if (!dp)
152 #ifndef CRUNCH
153 msg("%c%c not a digraph", extra[0], extra[1]);
154 #endif
155 return;
157 if (prev)
158 prev->next = dp->next;
159 else
160 digs = dp->next;
161 free(dp);
162 return;
165 /* if necessary, create a new digraph struct for the new digraph */
166 if (dig && !dp)
168 dp = (struct _DIG *)malloc(sizeof *dp);
169 if (!dp)
171 msg("Out of space in the digraph table");
172 return;
174 if (prev)
175 prev->next = dp;
176 else
177 digs = dp;
178 dp->next = (struct _DIG *)0;
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;
188 # ifndef NO_MKEXRC
189 void savedigs(fd)
190 int fd;
192 static char buf[] = "digraph! XX Y\n";
193 REG struct _DIG *dp;
195 for (dp = digs; dp; dp = dp->next)
197 if (dp->save)
199 buf[9] = dp->key1;
200 buf[10] = dp->key2;
201 buf[12] = dp->dig;
202 write(fd, buf, (unsigned)14);
206 # endif
207 #endif
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.
214 MARK 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? */
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
229 #ifdef DEBUG
230 /* if "from" and "to" are reversed, complain */
231 if (from > to)
233 msg("ERROR: input(%ld:%d, %ld:%d)",
234 markline(from), markidx(from),
235 markline(to), markidx(to));
236 return MARK_UNSET;
238 #endif
240 key[1] = 0;
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)
246 cut(from, to);
249 /* if doing a dot command, then reuse the previous text */
250 if (doingdot)
252 ChangeText
254 /* delete the text that's there now */
255 if (from != to)
257 delete(from, to);
260 /* insert the previous text */
261 cutname('.');
262 cursor = paste(from, FALSE, TRUE) + 1L;
265 else /* interactive version */
267 /* assume that whoever called this already did a beforedo() */
268 #ifndef NO_EXTENSIONS
269 inchg = TRUE;
270 #endif
272 /* if doing a change within the line... */
273 if (from != to && markline(from) == markline(to))
275 /* mark the end of the text with a "$" */
276 change(to - 1, to, "$");
278 else
280 /* delete the old text right off */
281 if (from != to)
283 delete(from, to);
285 to = from;
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)
294 /* Only autoindent blank lines. */
295 pfetch(markline(cursor));
296 if (plen == 0)
298 /* Okay, we really want to autoindent */
299 pfetch(markline(m));
300 for (scan = ptext, build = tmpblk.c;
301 *scan == ' ' || *scan == '\t';
304 *build++ = *scan++;
306 if (build > tmpblk.c)
308 *build = '\0';
309 add(cursor, tmpblk.c);
310 cursor += (build - tmpblk.c);
311 if (cursor > to)
312 to = cursor;
317 /* repeatedly add characters from the user */
318 for (;;)
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); )
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)
343 msg("Can't modify text during a selection");
344 beep();
345 continue;
347 #endif
349 #ifndef NO_EXTENSIONS
350 if (key[0] == ctrl('O'))
352 if (inchg)
354 if (cursor < to)
356 delete(cursor, to);
357 redraw(cursor, TRUE);
359 afterdo();
360 inchg = FALSE;
363 else if (key[0] != ctrl('['))
365 if (!inchg)
367 beforedo(FALSE);
368 inchg = TRUE;
371 #endif
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
378 if (*o_wrapmargin != 0)
380 pfetch(markline(cursor));
381 if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
383 build = tmpblk.c;
384 *build++ = '\n';
385 if (*o_autoindent)
387 /* figure out indent for next line */
388 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
390 *build++ = *scan++;
393 *build = '\0';
395 scan = ptext + plen;
396 m = cursor & ~(BLKSIZE - 1);
397 while (ptext < scan)
399 scan--;
400 if (*scan != ' ' && *scan != '\t')
401 continue;
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;
410 /*remove trailing spaces on previous line*/
411 pfetch(markline(m));
412 scan = ptext + plen;
413 while (ptext < scan)
415 scan--;
416 if (*scan != ' ' && *scan != '\t')
417 break;
419 delete(m + (scan-ptext) + 1, m + plen);
421 break;
425 #endif /* !CRUNCH */
427 /* process it */
428 switch (*key)
430 #ifndef NO_EXTENSIONS
431 case ctrl('O'): /* special movement mapped keys */
432 *key = getkey(0);
433 switch (*key)
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
456 m = v_xchar(cursor, 0L, 'x');
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--)
471 scan++;
472 m = (*scan ? v_keyword(scan, cursor, 0L) : cursor);
473 break;
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;
486 case 'd':
487 case 'y':
488 case '\\':
489 /* do nothing if unmarked */
490 if (!V_from)
492 msg("You must mark the text first");
493 beep();
494 break;
497 /* "from" must come before "to" */
498 if (V_from < cursor)
500 from = V_from;
501 to = cursor;
503 else
505 from = cursor;
506 to = V_from;
509 /* we don't need V_from anymore */
510 V_from = MARK_UNSET;
512 if (V_linemd)
514 /* adjust for line mode */
515 from &= ~(BLKSIZE - 1);
516 to |= (BLKSIZE - 1);
518 else
520 /* in character mode, we must
521 * worry about deleting the newline
522 * at the end of the last line
524 pfetch(markline(to));
525 if (markidx(to) == plen)
526 to |= (BLKSIZE - 1);
528 to++;
530 switch (*key)
532 case 'y':
533 cut(from, to);
534 break;
536 case 'd':
537 ChangeText
539 cut(from, to);
540 delete(from, to);
542 cursor = from;
543 break;
545 #ifndef NO_POPUP
546 case '\\':
547 ChangeText
549 cursor = v_popup(from, to);
551 break;
552 #endif
554 m = from = to = cursor;
555 break;
557 case 'p':
558 case 'P':
559 V_from = MARK_UNSET;
560 ChangeText
562 m = from = to = cursor = paste(cursor, (*key == 'p'), FALSE);
564 break;
565 # endif /* !NO_VISIBLE */
566 default: m = MARK_UNSET;
569 /* adjust the moved cursor */
570 if (m != cursor)
572 m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
573 if (*key == '$' || (*key == 'l' && m <= cursor))
575 m++;
579 /* if the cursor is reasonable, use it */
580 if (m == MARK_UNSET)
582 beep();
584 else
586 from = to = cursor = m;
588 break;
590 case ctrl('Z'):
591 if (getkey(0) == ctrl('Z'))
593 quit = TRUE;
594 goto BreakBreak;
596 break;
597 #endif
599 case ctrl('['):
600 /* if last line contains only whitespace, then remove whitespace */
601 if (*o_autoindent)
603 pfetch(markline(cursor));
604 for (scan = ptext; isspace(*scan); scan++)
607 if (scan > ptext && !*scan)
609 cursor &= ~(BLKSIZE - 1L);
610 if (to < cursor + plen)
612 to = cursor + plen;
616 goto BreakBreak;
618 case ctrl('U'):
619 if (markline(cursor) == markline(from))
621 cursor = from;
623 else
625 cursor &= ~(BLKSIZE - 1);
627 break;
629 case ctrl('D'):
630 case ctrl('T'):
631 if (to > cursor)
633 delete(cursor, to);
635 mark[27] = cursor;
636 cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
637 if (mark[27])
639 cursor = mark[27];
641 else
643 cursor = m_front(cursor, 0L);
645 to = cursor;
646 break;
648 case '\b':
649 if (cursor <= from)
651 beep();
653 else if (markidx(cursor) == 0)
655 cursor -= BLKSIZE;
656 pfetch(markline(cursor));
657 cursor += plen;
659 else
661 cursor--;
663 break;
665 case ctrl('W'):
666 m = m_bword(cursor, 1L, 'b');
667 if (markline(m) == markline(cursor) && m >= from)
669 cursor = m;
670 if (from > cursor)
672 from = cursor;
675 else
677 beep();
679 break;
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)
691 /* figure out indent for next line */
692 pfetch(markline(cursor));
693 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
695 *build++ = *scan++;
698 /* remove indent from this line, if blank */
699 if ((scan - ptext) >= markidx(cursor) && plen > 0)
701 to = cursor &= ~(BLKSIZE - 1);
702 delete(cursor, cursor + plen);
705 *build = 0;
706 if (cursor >= to && when != WHEN_VIREP)
708 add(cursor, tmpblk.c);
710 else
712 change(cursor, to, tmpblk.c);
714 redraw(cursor, TRUE);
715 to = cursor = (cursor & ~(BLKSIZE - 1))
716 + BLKSIZE
717 + (int)(build - tmpblk.c) - 1;
718 break;
720 case ctrl('A'):
721 case ctrl('P'):
722 if (cursor < to)
724 delete(cursor, to);
726 if (*key == ctrl('A'))
728 cutname('.');
730 to = cursor = paste(cursor, FALSE, TRUE) + 1L;
731 break;
733 case ctrl('V'):
734 if (cursor >= to && when != WHEN_VIREP)
736 add(cursor, "^");
738 else
740 change(cursor, to, "^");
741 to = cursor + 1;
743 redraw(cursor, TRUE);
744 *key = getkey(0);
745 if (*key == '\n')
747 /* '\n' too hard to handle */
748 #if OSK
749 *key = '\l';
750 #else
751 *key = '\r';
752 #endif
754 change(cursor, cursor + 1, key);
755 cursor++;
756 if (cursor > to)
758 to = cursor;
760 break;
762 case ctrl('L'):
763 case ctrl('R'):
764 redraw(MARK_UNSET, FALSE);
765 break;
767 default:
768 if (cursor >= to && when != WHEN_VIREP)
770 add(cursor, key);
771 cursor++;
772 to = cursor;
774 else
776 pfetch(markline(cursor));
777 if (markidx(cursor) == plen)
779 add(cursor, key);
781 else
783 #ifndef NO_DIGRAPH
784 *key = digraph(ptext[markidx(cursor)], *key);
785 #endif
786 change(cursor, cursor + 1, key);
788 cursor++;
790 #ifndef NO_SHOWMATCH
791 /* show matching "({[" if necessary */
792 if (*o_showmatch && strchr(")}]", *key))
794 redraw(cursor, TRUE);
795 m = m_match(cursor - 1, 0L);
796 if (markline(m) >= topline
797 && markline(m) <= botline)
799 redraw(m, TRUE);
800 refresh();
801 sleep(1);
804 #endif
805 } /* end switch(*key) */
806 } /* end for(;;) */
807 BreakBreak:;
808 /* delete any excess characters */
809 if (cursor < to)
811 #ifndef NO_EXTENSIONS
812 /* if we aren't in the middle of a change, start one! */
813 if (!inchg)
815 beforedo(FALSE);
816 inchg = TRUE;
818 #endif
819 delete(cursor, to);
822 } /* end if doingdot else */
824 /* put the new text into a cut buffer for possible reuse */
825 if (!doingdot)
827 blksync();
828 cutname('.');
829 cut(from, cursor);
832 /* move to last char that we inputted, unless it was newline */
833 if (markidx(cursor) != 0)
835 cursor--;
837 redraw(cursor, FALSE);
839 #ifndef NO_EXTENSIONS
840 if (quit)
842 /* if this is a nested "do", then cut it short */
843 abortdo();
845 /* exit, unless we can't write out the file */
846 cursor = v_xit(cursor, 0L, 'Z');
848 #endif
850 rptlines = 0L;
851 return cursor;