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