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