add destdin and vgrindt
[unix-history] / usr / src / usr.bin / ex / ex_vops2.c
CommitLineData
7c4625ef 1/* Copyright (c) 1980 Regents of the University of California */
b41bbcb5 2static char *sccsid = "@(#)ex_vops2.c 5.1 %G%";
09035cec
MH
3#include "ex.h"
4#include "ex_tty.h"
5#include "ex_vis.h"
6
7/*
8 * Low level routines for operations sequences,
9 * and mostly, insert mode (and a subroutine
10 * to read an input line, including in the echo area.)
11 */
12char *vUA1, *vUA2;
13char *vUD1, *vUD2;
14
15/*
16 * Obleeperate characters in hardcopy
17 * open with \'s.
18 */
19bleep(i, cp)
20 register int i;
21 char *cp;
22{
23
24 i -= column(cp);
25 do
26 putchar('\\' | QUOTE);
27 while (--i >= 0);
28 rubble = 1;
29}
30
31/*
32 * Common code for middle part of delete
33 * and change operating on parts of lines.
34 */
35vdcMID()
36{
37 register char *cp;
38
39 squish();
40 setLAST();
887e3e0d
MH
41 if (FIXUNDO)
42 vundkind = VCHNG, CP(vutmp, linebuf);
09035cec
MH
43 if (wcursor < cursor)
44 cp = wcursor, wcursor = cursor, cursor = cp;
45 vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
46 return (column(wcursor - 1));
47}
48
49/*
50 * Take text from linebuf and stick it
51 * in the VBSIZE buffer BUF. Used to save
52 * deleted text of part of line.
53 */
54takeout(BUF)
55 char *BUF;
56{
57 register char *cp;
58
59 if (wcursor < linebuf)
60 wcursor = linebuf;
61 if (cursor == wcursor) {
62 beep();
63 return;
64 }
65 if (wcursor < cursor) {
66 cp = wcursor;
67 wcursor = cursor;
68 cursor = cp;
69 }
70 setBUF(BUF);
71 if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
72 beep();
73}
74
75/*
76 * Are we at the end of the printed representation of the
77 * line? Used internally in hardcopy open.
78 */
79ateopr()
80{
81 register int i, c;
82 register char *cp = vtube[destline] + destcol;
83
84 for (i = WCOLS - destcol; i > 0; i--) {
85 c = *cp++;
86 if (c == 0)
87 return (1);
88 if (c != ' ' && (c & QUOTE) == 0)
89 return (0);
90 }
91 return (1);
92}
93
94/*
95 * Append.
96 *
97 * This routine handles the top level append, doing work
98 * as each new line comes in, and arranging repeatability.
99 * It also handles append with repeat counts, and calculation
100 * of autoindents for new lines.
101 */
102bool vaifirst;
103bool gobbled;
104char *ogcursor;
105
106vappend(ch, cnt, indent)
107 char ch;
108 int cnt, indent;
109{
110 register int i;
111 register char *gcursor;
112 bool escape;
113 int repcnt;
114 short oldhold = hold;
115
116 /*
117 * Before a move in hardopen when the line is dirty
118 * or we are in the middle of the printed representation,
119 * we retype the line to the left of the cursor so the
120 * insert looks clean.
121 */
122 if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
123 rubble = 1;
124 gcursor = cursor;
125 i = *gcursor;
126 *gcursor = ' ';
127 wcursor = gcursor;
128 vmove();
129 *gcursor = i;
130 }
131 vaifirst = indent == 0;
132
133 /*
134 * Handle replace character by (eventually)
135 * limiting the number of input characters allowed
136 * in the vgetline routine.
137 */
138 if (ch == 'r')
139 repcnt = 2;
140 else
141 repcnt = 0;
142
143 /*
144 * If an autoindent is specified, then
145 * generate a mixture of blanks to tabs to implement
146 * it and place the cursor after the indent.
147 * Text read by the vgetline routine will be placed in genbuf,
148 * so the indent is generated there.
149 */
150 if (value(AUTOINDENT) && indent != 0) {
151 gcursor = genindent(indent);
152 *gcursor = 0;
153 vgotoCL(qcolumn(cursor - 1, genbuf));
154 } else {
155 gcursor = genbuf;
156 *gcursor = 0;
157 if (ch == 'o')
158 vfixcurs();
159 }
160
161 /*
162 * Prepare for undo. Pointers delimit inserted portion of line.
163 */
164 vUA1 = vUA2 = cursor;
165
166 /*
167 * If we are not in a repeated command and a ^@ comes in
168 * then this means the previous inserted text.
169 * If there is none or it was too long to be saved,
170 * then beep() and also arrange to undo any damage done
171 * so far (e.g. if we are a change.)
172 */
173 if ((vglobp && *vglobp == 0) || peekbr()) {
174 if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
175 beep();
176 if (!splitw)
177 ungetkey('u');
178 doomed = 0;
179 hold = oldhold;
180 return;
181 }
182 /*
183 * Unread input from INS.
184 * An escape will be generated at end of string.
185 * Hold off n^^2 type update on dumb terminals.
186 */
187 vglobp = INS;
188 hold |= HOLDQIK;
189 } else if (vglobp == 0)
190 /*
191 * Not a repeated command, get
192 * a new inserted text for repeat.
193 */
194 INS[0] = 0;
195
196 /*
197 * For wrapmargin to hack away second space after a '.'
198 * when the first space caused a line break we keep
199 * track that this happened in gobblebl, which says
200 * to gobble up a blank silently.
201 */
202 gobblebl = 0;
203
204 /*
205 * Text gathering loop.
206 * New text goes into genbuf starting at gcursor.
207 * cursor preserves place in linebuf where text will eventually go.
208 */
209 if (*cursor == 0 || state == CRTOPEN)
210 hold |= HOLDROL;
211 for (;;) {
212 if (ch == 'r' && repcnt == 0)
213 escape = 0;
214 else {
215 gcursor = vgetline(repcnt, gcursor, &escape);
216
217 /*
218 * After an append, stick information
219 * about the ^D's and ^^D's and 0^D's in
220 * the repeated text buffer so repeated
221 * inserts of stuff indented with ^D as backtab's
222 * can work.
223 */
224 if (HADUP)
225 addtext("^");
226 else if (HADZERO)
227 addtext("0");
228 while (CDCNT > 0)
229 addtext("\204"), CDCNT--;
230 if (gobbled)
231 addtext(" ");
232 addtext(ogcursor);
233 }
234 repcnt = 0;
235
236 /*
237 * Smash the generated and preexisting indents together
238 * and generate one cleanly made out of tabs and spaces
239 * if we are using autoindent.
240 */
241 if (!vaifirst && value(AUTOINDENT)) {
242 i = fixindent(indent);
243 if (!HADUP)
244 indent = i;
245 gcursor = strend(genbuf);
246 }
247
248 /*
249 * Limit the repetition count based on maximum
250 * possible line length; do output implied
251 * by further count (> 1) and cons up the new line
252 * in linebuf.
253 */
254 cnt = vmaxrep(ch, cnt);
255 CP(gcursor + 1, cursor);
256 do {
257 CP(cursor, genbuf);
258 if (cnt > 1) {
259 int oldhold = hold;
260
261 Outchar = vinschar;
262 hold |= HOLDQIK;
263 printf("%s", genbuf);
264 hold = oldhold;
265 Outchar = vputchar;
266 }
267 cursor += gcursor - genbuf;
268 } while (--cnt > 0);
269 endim();
270 vUA2 = cursor;
271 if (escape != '\n')
272 CP(cursor, gcursor + 1);
273
274 /*
275 * If doomed characters remain, clobber them,
276 * and reopen the line to get the display exact.
277 */
278 if (state != HARDOPEN) {
279 DEPTH(vcline) = 0;
280 if (doomed > 0) {
281 register int cind = cindent();
282
283 physdc(cind, cind + doomed);
284 doomed = 0;
285 }
286 i = vreopen(LINE(vcline), lineDOT(), vcline);
287 }
288
289 /*
290 * All done unless we are continuing on to another line.
291 */
292 if (escape != '\n')
293 break;
294
295 /*
296 * Set up for the new line.
297 * First save the current line, then construct a new
298 * first image for the continuation line consisting
299 * of any new autoindent plus the pushed ahead text.
300 */
301 killU();
302 addtext(gobblebl ? " " : "\n");
303 vsave();
304 cnt = 1;
305 if (value(AUTOINDENT)) {
306#ifdef LISPCODE
307 if (value(LISP))
308 indent = lindent(dot + 1);
309 else
310#endif
311 if (!HADUP && vaifirst)
312 indent = whitecnt(linebuf);
313 vaifirst = 0;
314 strcLIN(vpastwh(gcursor + 1));
315 gcursor = genindent(indent);
316 *gcursor = 0;
317 if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
318 gcursor = genbuf;
319 CP(gcursor, linebuf);
320 } else {
321 CP(genbuf, gcursor + 1);
322 gcursor = genbuf;
323 }
324
325 /*
326 * If we started out as a single line operation and are now
327 * turning into a multi-line change, then we had better yank
328 * out dot before it changes so that undo will work
329 * correctly later.
330 */
887e3e0d 331 if (FIXUNDO && vundkind == VCHNG) {
09035cec
MH
332 vremote(1, yank, 0);
333 undap1--;
334 }
335
336 /*
337 * Now do the append of the new line in the buffer,
338 * and update the display. If slowopen
339 * we don't do very much.
340 */
341 vdoappend(genbuf);
342 vundkind = VMANYINS;
343 vcline++;
344 if (state != VISUAL)
345 vshow(dot, NOLINE);
346 else {
347 i += LINE(vcline - 1);
348 vopen(dot, i);
349 if (value(SLOWOPEN))
350 vscrap();
351 else
352 vsync1(LINE(vcline));
353 }
354 strcLIN(gcursor);
355 *gcursor = 0;
356 cursor = linebuf;
357 vgotoCL(qcolumn(cursor - 1, genbuf));
358 }
359
360 /*
361 * All done with insertion, position the cursor
362 * and sync the screen.
363 */
364 hold = oldhold;
365 if (cursor > linebuf)
366 cursor--;
367 if (state != HARDOPEN)
368 vsyncCL();
369 else if (cursor > linebuf)
370 back1();
371 doomed = 0;
372 wcursor = cursor;
373 vmove();
374}
375
376/*
377 * Subroutine for vgetline to back up a single character position,
378 * backwards around end of lines (vgoto can't hack columns which are
379 * less than 0 in general).
380 */
381back1()
382{
383
384 vgoto(destline - 1, WCOLS + destcol - 1);
385}
386
387/*
388 * Get a line into genbuf after gcursor.
389 * Cnt limits the number of input characters
390 * accepted and is used for handling the replace
391 * single character command. Aescaped is the location
392 * where we stick a termination indicator (whether we
393 * ended with an ESCAPE or a newline/return.
394 *
395 * We do erase-kill type processing here and also
396 * are careful about the way we do this so that it is
397 * repeatable. (I.e. so that your kill doesn't happen,
398 * when you repeat an insert if it was escaped with \ the
399 * first time you did it.
400 */
401char *
402vgetline(cnt, gcursor, aescaped)
403 int cnt;
404 register char *gcursor;
405 bool *aescaped;
406{
407 register int c, ch;
408 register char *cp;
409 int x, y, iwhite;
410 char *iglobp;
d266c416 411 char cstr[2];
09035cec
MH
412 int (*OO)() = Outchar;
413
414 /*
415 * Clear the output state and counters
416 * for autoindent backwards motion (counts of ^D, etc.)
417 * Remember how much white space at beginning of line so
418 * as not to allow backspace over autoindent.
419 */
420 *aescaped = 0;
421 ogcursor = gcursor;
422 flusho();
423 CDCNT = 0;
424 HADUP = 0;
425 HADZERO = 0;
426 gobbled = 0;
427 iwhite = whitecnt(genbuf);
428 iglobp = vglobp;
429
430 /*
431 * Carefully avoid using vinschar in the echo area.
432 */
433 if (splitw)
434 Outchar = vputchar;
435 else {
436 Outchar = vinschar;
437 vprepins();
438 }
439 for (;;) {
440 if (gobblebl)
441 gobblebl--;
442 if (cnt != 0) {
443 cnt--;
444 if (cnt == 0)
445 goto vadone;
446 }
d266c416
MH
447 c = getkey();
448 if (c != ATTN)
449 c &= (QUOTE|TRIM);
450 ch = c;
451 maphopcnt = 0;
887e3e0d
MH
452 if (vglobp == 0 && Peekkey == 0)
453 while ((ch = map(c, immacs)) != c) {
09035cec 454 c = ch;
887e3e0d
MH
455 if (!value(REMAP))
456 break;
d266c416
MH
457 if (++maphopcnt > 256)
458 error("Infinite macro loop");
887e3e0d 459 }
09035cec
MH
460 if (!iglobp) {
461
462 /*
463 * Erase-kill type processing.
464 * Only happens if we were not reading
465 * from untyped input when we started.
466 * Map users erase to ^H, kill to -1 for switch.
467 */
d266c416 468#ifndef USG3TTY
09035cec
MH
469 if (c == tty.sg_erase)
470 c = CTRL(h);
471 else if (c == tty.sg_kill)
472 c = -1;
d266c416
MH
473#else
474 if (c == tty.c_cc[VERASE])
475 c = CTRL(h);
476 else if (c == tty.c_cc[VKILL])
477 c = -1;
478#endif
09035cec
MH
479 switch (c) {
480
481 /*
482 * ^? Interrupt drops you back to visual
483 * command mode with an unread interrupt
484 * still in the input buffer.
485 *
486 * ^\ Quit does the same as interrupt.
487 * If you are a ex command rather than
488 * a vi command this will drop you
489 * back to command mode for sure.
490 */
491 case ATTN:
492 case QUIT:
493 ungetkey(c);
494 goto vadone;
495
496 /*
497 * ^H Backs up a character in the input.
498 *
499 * BUG: Can't back around line boundaries.
500 * This is hard because stuff has
501 * already been saved for repeat.
502 */
503 case CTRL(h):
504bakchar:
505 cp = gcursor - 1;
506 if (cp < ogcursor) {
507 if (splitw) {
508 /*
509 * Backspacing over readecho
510 * prompt. Pretend delete but
511 * don't beep.
512 */
513 ungetkey(c);
514 goto vadone;
515 }
516 beep();
517 continue;
518 }
519 goto vbackup;
520
521 /*
522 * ^W Back up a white/non-white word.
523 */
524 case CTRL(w):
525 wdkind = 1;
526 for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
527 continue;
528 for (c = wordch(cp - 1);
529 cp > ogcursor && wordof(c, cp - 1); cp--)
530 continue;
531 goto vbackup;
532
533 /*
534 * users kill Kill input on this line, back to
535 * the autoindent.
536 */
537 case -1:
538 cp = ogcursor;
539vbackup:
540 if (cp == gcursor) {
541 beep();
542 continue;
543 }
544 endim();
545 *cp = 0;
546 c = cindent();
547 vgotoCL(qcolumn(cursor - 1, genbuf));
548 if (doomed >= 0)
549 doomed += c - cindent();
550 gcursor = cp;
551 continue;
552
553 /*
554 * \ Followed by erase or kill
555 * maps to just the erase or kill.
556 */
557 case '\\':
558 x = destcol, y = destline;
559 putchar('\\');
560 vcsync();
561 c = getkey();
d266c416 562#ifndef USG3TTY
09035cec 563 if (c == tty.sg_erase || c == tty.sg_kill) {
d266c416
MH
564#else
565 if (c == tty.c_cc[VERASE]
566 || c == tty.c_cc[VKILL]) {
567#endif
09035cec
MH
568 vgoto(y, x);
569 if (doomed >= 0)
570 doomed++;
571 goto def;
572 }
573 ungetkey(c), c = '\\';
574 goto noput;
575
576 /*
577 * ^Q Super quote following character
578 * Only ^@ is verboten (trapped at
579 * a lower level) and \n forces a line
580 * split so doesn't really go in.
581 *
582 * ^V Synonym for ^Q
583 */
584 case CTRL(q):
585 case CTRL(v):
586 x = destcol, y = destline;
587 putchar('^');
588 vgoto(y, x);
589 c = getkey();
590#ifdef TIOCSETC
591 if (c == ATTN)
592 c = nttyc.t_intrc;
593#endif
594 if (c != NL) {
595 if (doomed >= 0)
596 doomed++;
597 goto def;
598 }
599 break;
600 }
601 }
602
603 /*
604 * If we get a blank not in the echo area
605 * consider splitting the window in the wrapmargin.
606 */
d266c416
MH
607 if (c != NL && !splitw) {
608 if (c == ' ' && gobblebl) {
09035cec
MH
609 gobbled = 1;
610 continue;
611 }
d266c416
MH
612 if (/* c <= ' ' && */ value(WRAPMARGIN) &&
613 outcol >= OCOLUMNS - value(WRAPMARGIN)) {
614 /*
615 * At end of word and hit wrapmargin.
616 * Move the word to next line and keep going.
617 */
618 wdkind = 1;
619 *gcursor++ = c;
620 *gcursor = 0;
621 /*
622 * Find end of previous word if we are past it.
623 */
624 for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--)
625 ;
626 if (outcol - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
627 /*
628 * Find beginning of previous word.
629 */
630 for (; cp>ogcursor && !isspace(cp[-1]); cp--)
631 ;
632 if (cp <= ogcursor) {
633 /*
634 * There is a single word that
635 * is too long to fit. Just
636 * let it pass, but beep for
637 * each new letter to warn
638 * the luser.
639 */
640 c = *--gcursor;
641 *gcursor = 0;
642 beep();
643 goto dontbreak;
644 }
645 /*
646 * Save it for next line.
647 */
648 macpush(cp, 0);
649 cp--;
650 }
651 macpush("\n", 0);
652 /*
653 * Erase white space before the word.
654 */
655 while (cp > ogcursor && isspace(cp[-1]))
656 cp--; /* skip blank */
657 gobblebl = 3;
658 goto vbackup;
09035cec 659 }
d266c416
MH
660 dontbreak:;
661 }
662
663 /*
664 * Word abbreviation mode.
665 */
666 cstr[0] = c;
667 if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
668 int wdtype, abno;
669
670 cstr[1] = 0;
671 wdkind = 1;
672 cp = gcursor - 1;
673 for (wdtype = wordch(cp - 1);
674 cp > ogcursor && wordof(wdtype, cp - 1); cp--)
675 ;
676 *gcursor = 0;
677 for (abno=0; abbrevs[abno].mapto; abno++) {
678 if (eq(cp, abbrevs[abno].cap)) {
679 macpush(cstr, 0);
680 macpush(abbrevs[abno].mapto);
681 goto vbackup;
682 }
683 }
09035cec 684 }
d266c416 685
09035cec
MH
686 switch (c) {
687
688 /*
689 * ^M Except in repeat maps to \n.
690 */
691 case CR:
692 if (vglobp)
693 goto def;
694 c = '\n';
695 /* presto chango ... */
696
697 /*
698 * \n Start new line.
699 */
700 case NL:
701 *aescaped = c;
702 goto vadone;
703
704 /*
705 * escape End insert unless repeat and more to repeat.
706 */
707 case ESCAPE:
708 if (lastvgk)
709 goto def;
710 goto vadone;
711
712 /*
713 * ^D Backtab.
714 * ^T Software forward tab.
715 *
716 * Unless in repeat where this means these
717 * were superquoted in.
718 */
719 case CTRL(d):
720 case CTRL(t):
721 if (vglobp)
722 goto def;
723 /* fall into ... */
724
725 /*
726 * ^D|QUOTE Is a backtab (in a repeated command).
727 */
728 case CTRL(d) | QUOTE:
729 *gcursor = 0;
730 cp = vpastwh(genbuf);
731 c = whitecnt(genbuf);
732 if (ch == CTRL(t)) {
733 /*
734 * ^t just generates new indent replacing
735 * current white space rounded up to soft
736 * tab stop increment.
737 */
738 if (cp != gcursor)
739 /*
740 * BUG: Don't hack ^T except
741 * right after initial
742 * white space.
743 */
744 continue;
745 cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
746 ogcursor = cp;
747 goto vbackup;
748 }
749 /*
750 * ^D works only if we are at the (end of) the
751 * generated autoindent. We count the ^D for repeat
752 * purposes.
753 */
754 if (c == iwhite && c != 0)
755 if (cp == gcursor) {
756 iwhite = backtab(c);
757 CDCNT++;
758 ogcursor = cp = genindent(iwhite);
759 goto vbackup;
760 } else if (&cp[1] == gcursor &&
761 (*cp == '^' || *cp == '0')) {
762 /*
763 * ^^D moves to margin, then back
764 * to current indent on next line.
765 *
766 * 0^D moves to margin and then
767 * stays there.
768 */
769 HADZERO = *cp == '0';
770 ogcursor = cp = genbuf;
771 HADUP = 1 - HADZERO;
772 CDCNT = 1;
773 endim();
774 back1();
887e3e0d 775 vputchar(' ');
09035cec
MH
776 goto vbackup;
777 }
778 if (vglobp && vglobp - iglobp >= 2 &&
779 (vglobp[-2] == '^' || vglobp[-2] == '0')
780 && gcursor == ogcursor + 1)
781 goto bakchar;
782 continue;
783
784 default:
785 /*
786 * Possibly discard control inputs.
787 */
788 if (!vglobp && junk(c)) {
789 beep();
790 continue;
791 }
792def:
793 putchar(c);
d266c416 794 flush();
09035cec
MH
795noput:
796 if (gcursor > &genbuf[LBSIZE - 2])
797 error("Line too long");
798 *gcursor++ = c & TRIM;
799 vcsync();
09035cec
MH
800 if (value(SHOWMATCH) && !iglobp)
801 if (c == ')' || c == '}')
802 lsmatch(gcursor);
09035cec
MH
803 continue;
804 }
805 }
806vadone:
807 *gcursor = 0;
d266c416
MH
808 if (Outchar != termchar)
809 Outchar = OO;
09035cec
MH
810 endim();
811 return (gcursor);
812}
813
814int vgetsplit();
815char *vsplitpt;
816
817/*
818 * Append the line in buffer at lp
819 * to the buffer after dot.
820 */
821vdoappend(lp)
822 char *lp;
823{
824 register int oing = inglobal;
825
826 vsplitpt = lp;
827 inglobal = 1;
828 ignore(append(vgetsplit, dot));
829 inglobal = oing;
830}
831
832/*
833 * Subroutine for vdoappend to pass to append.
834 */
835vgetsplit()
836{
837
838 if (vsplitpt == 0)
839 return (EOF);
840 strcLIN(vsplitpt);
841 vsplitpt = 0;
842 return (0);
843}
844
845/*
846 * Vmaxrep determines the maximum repetitition factor
847 * allowed that will yield total line length less than
44232d5b 848 * LBSIZE characters and also does hacks for the R command.
09035cec
MH
849 */
850vmaxrep(ch, cnt)
851 char ch;
852 register int cnt;
853{
854 register int len, replen;
855
856 if (cnt > LBSIZE - 2)
857 cnt = LBSIZE - 2;
858 replen = strlen(genbuf);
859 if (ch == 'R') {
860 len = strlen(cursor);
861 if (replen < len)
862 len = replen;
863 CP(cursor, cursor + len);
864 vUD2 += len;
865 }
866 len = strlen(linebuf);
867 if (len + cnt * replen <= LBSIZE - 2)
868 return (cnt);
869 cnt = (LBSIZE - 2 - len) / replen;
870 if (cnt == 0) {
871 vsave();
872 error("Line too long");
873 }
874 return (cnt);
875}