|
|
1.1 root 1: /* Copyright (c) 1981 Regents of the University of California */
2: static char *sccsid = "@(#)ex_vops2.c 6.5 7/26/81";
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: */
12: extern char *vUA1, *vUA2; /* mjm: extern; also in ex_vops.c */
13: extern char *vUD1, *vUD2; /* mjm: extern; also in ex_vops.c */
14:
15: /*
16: * Obleeperate characters in hardcopy
17: * open with \'s.
18: */
19: bleep(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: */
35: vdcMID()
36: {
37: register char *cp;
38:
39: squish();
40: setLAST();
41: if (FIXUNDO)
42: vundkind = VCHNG, CP(vutmp, linebuf);
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: */
54: takeout(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: */
79: ateopr()
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: */
102: bool vaifirst;
103: bool gobbled;
104: char *ogcursor;
105:
106: vappend(ch, cnt, indent)
107: int ch; /* mjm: char --> int */
108: int cnt, indent;
109: {
110: register int i;
111: register char *gcursor;
112: bool escape;
113: int repcnt, savedoomed;
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, ch);
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: savedoomed = doomed;
281: if (doomed > 0) {
282: register int cind = cindent();
283:
284: physdc(cind, cind + doomed);
285: doomed = 0;
286: }
287: i = vreopen(LINE(vcline), lineDOT(), vcline);
288: #ifdef TRACE
289: if (trace)
290: fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
291: #endif
292: if (ch == 'R')
293: doomed = savedoomed;
294: }
295:
296: /*
297: * All done unless we are continuing on to another line.
298: */
299: if (escape != '\n')
300: break;
301:
302: /*
303: * Set up for the new line.
304: * First save the current line, then construct a new
305: * first image for the continuation line consisting
306: * of any new autoindent plus the pushed ahead text.
307: */
308: killU();
309: addtext(gobblebl ? " " : "\n");
310: vsave();
311: cnt = 1;
312: if (value(AUTOINDENT)) {
313: #ifdef LISPCODE
314: if (value(LISP))
315: indent = lindent(dot + 1);
316: else
317: #endif
318: if (!HADUP && vaifirst)
319: indent = whitecnt(linebuf);
320: vaifirst = 0;
321: strcLIN(vpastwh(gcursor + 1));
322: gcursor = genindent(indent);
323: *gcursor = 0;
324: if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
325: gcursor = genbuf;
326: CP(gcursor, linebuf);
327: } else {
328: CP(genbuf, gcursor + 1);
329: gcursor = genbuf;
330: }
331:
332: /*
333: * If we started out as a single line operation and are now
334: * turning into a multi-line change, then we had better yank
335: * out dot before it changes so that undo will work
336: * correctly later.
337: */
338: if (FIXUNDO && vundkind == VCHNG) {
339: vremote(1, yank, 0);
340: undap1--;
341: }
342:
343: /*
344: * Now do the append of the new line in the buffer,
345: * and update the display. If slowopen
346: * we don't do very much.
347: */
348: vdoappend(genbuf);
349: vundkind = VMANYINS;
350: vcline++;
351: if (state != VISUAL)
352: vshow(dot, NOLINE);
353: else {
354: i += LINE(vcline - 1);
355: vopen(dot, i);
356: if (value(SLOWOPEN))
357: vscrap();
358: else
359: vsync1(LINE(vcline));
360: }
361: strcLIN(gcursor);
362: *gcursor = 0;
363: cursor = linebuf;
364: vgotoCL(qcolumn(cursor - 1, genbuf));
365: }
366:
367: /*
368: * All done with insertion, position the cursor
369: * and sync the screen.
370: */
371: hold = oldhold;
372: if (cursor > linebuf)
373: cursor--;
374: if (state != HARDOPEN)
375: vsyncCL();
376: else if (cursor > linebuf)
377: back1();
378: doomed = 0;
379: wcursor = cursor;
380: vmove();
381: }
382:
383: /*
384: * Subroutine for vgetline to back up a single character position,
385: * backwards around end of lines (vgoto can't hack columns which are
386: * less than 0 in general).
387: */
388: back1()
389: {
390:
391: vgoto(destline - 1, WCOLS + destcol - 1);
392: }
393:
394: /*
395: * Get a line into genbuf after gcursor.
396: * Cnt limits the number of input characters
397: * accepted and is used for handling the replace
398: * single character command. Aescaped is the location
399: * where we stick a termination indicator (whether we
400: * ended with an ESCAPE or a newline/return.
401: *
402: * We do erase-kill type processing here and also
403: * are careful about the way we do this so that it is
404: * repeatable. (I.e. so that your kill doesn't happen,
405: * when you repeat an insert if it was escaped with \ the
406: * first time you did it. commch is the command character
407: * involved, including the prompt for readline.
408: */
409: char *
410: vgetline(cnt, gcursor, aescaped, commch)
411: int cnt;
412: register char *gcursor;
413: bool *aescaped;
414: char commch;
415: {
416: register int c, ch;
417: register char *cp;
418: int x, y, iwhite, backsl=0;
419: char *iglobp;
420: char cstr[2];
421: int (*OO)() = Outchar;
422:
423: /*
424: * Clear the output state and counters
425: * for autoindent backwards motion (counts of ^D, etc.)
426: * Remember how much white space at beginning of line so
427: * as not to allow backspace over autoindent.
428: */
429: *aescaped = 0;
430: ogcursor = gcursor;
431: flusho();
432: CDCNT = 0;
433: HADUP = 0;
434: HADZERO = 0;
435: gobbled = 0;
436: iwhite = whitecnt(genbuf);
437: iglobp = vglobp;
438:
439: /*
440: * Carefully avoid using vinschar in the echo area.
441: */
442: if (splitw)
443: Outchar = vputchar;
444: else {
445: Outchar = vinschar;
446: vprepins();
447: }
448: for (;;) {
449: backsl = 0;
450: if (gobblebl)
451: gobblebl--;
452: if (cnt != 0) {
453: cnt--;
454: if (cnt == 0)
455: goto vadone;
456: }
457: c = getkey();
458: if (c != ATTN)
459: c &= (QUOTE|TRIM);
460: ch = c;
461: maphopcnt = 0;
462: if (vglobp == 0 && Peekkey == 0 && commch != 'r')
463: while ((ch = map(c, immacs)) != c) {
464: c = ch;
465: if (!value(REMAP))
466: break;
467: if (++maphopcnt > 256)
468: error("Infinite macro loop");
469: }
470: if (!iglobp) {
471:
472: /*
473: * Erase-kill type processing.
474: * Only happens if we were not reading
475: * from untyped input when we started.
476: * Map users erase to ^H, kill to -1 for switch.
477: */
478: #ifndef USG3TTY
479: if (c == tty.sg_erase)
480: c = CTRL(h);
481: else if (c == tty.sg_kill)
482: c = -1;
483: #else
484: if (c == tty.c_cc[VERASE])
485: c = CTRL(h);
486: else if (c == tty.c_cc[VKILL])
487: c = -1;
488: #endif
489: switch (c) {
490:
491: /*
492: * ^? Interrupt drops you back to visual
493: * command mode with an unread interrupt
494: * still in the input buffer.
495: *
496: * ^\ Quit does the same as interrupt.
497: * If you are a ex command rather than
498: * a vi command this will drop you
499: * back to command mode for sure.
500: */
501: case ATTN:
502: case QUIT:
503: ungetkey(c);
504: goto vadone;
505:
506: /*
507: * ^H Backs up a character in the input.
508: *
509: * BUG: Can't back around line boundaries.
510: * This is hard because stuff has
511: * already been saved for repeat.
512: */
513: case CTRL(h):
514: bakchar:
515: cp = gcursor - 1;
516: if (cp < ogcursor) {
517: if (splitw) {
518: /*
519: * Backspacing over readecho
520: * prompt. Pretend delete but
521: * don't beep.
522: */
523: ungetkey(c);
524: goto vadone;
525: }
526: beep();
527: continue;
528: }
529: goto vbackup;
530:
531: /*
532: * ^W Back up a white/non-white word.
533: */
534: case CTRL(w):
535: wdkind = 1;
536: for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
537: continue;
538: for (c = wordch(cp - 1);
539: cp > ogcursor && wordof(c, cp - 1); cp--)
540: continue;
541: goto vbackup;
542:
543: /*
544: * users kill Kill input on this line, back to
545: * the autoindent.
546: */
547: case -1:
548: cp = ogcursor;
549: vbackup:
550: if (cp == gcursor) {
551: beep();
552: continue;
553: }
554: endim();
555: *cp = 0;
556: c = cindent();
557: vgotoCL(qcolumn(cursor - 1, genbuf));
558: if (doomed >= 0)
559: doomed += c - cindent();
560: gcursor = cp;
561: continue;
562:
563: /*
564: * \ Followed by erase or kill
565: * maps to just the erase or kill.
566: */
567: case '\\':
568: x = destcol, y = destline;
569: putchar('\\');
570: vcsync();
571: c = getkey();
572: #ifndef USG3TTY
573: if (c == tty.sg_erase || c == tty.sg_kill)
574: #else
575: if (c == tty.c_cc[VERASE]
576: || c == tty.c_cc[VKILL])
577: #endif
578: {
579: vgoto(y, x);
580: if (doomed >= 0)
581: doomed++;
582: goto def;
583: }
584: ungetkey(c), c = '\\';
585: backsl = 1;
586: break;
587:
588: /*
589: * ^Q Super quote following character
590: * Only ^@ is verboten (trapped at
591: * a lower level) and \n forces a line
592: * split so doesn't really go in.
593: *
594: * ^V Synonym for ^Q
595: */
596: case CTRL(q):
597: case CTRL(v):
598: x = destcol, y = destline;
599: putchar('^');
600: vgoto(y, x);
601: c = getkey();
602: #ifdef TIOCSETC
603: if (c == ATTN)
604: c = nttyc.t_intrc;
605: #endif
606: if (c != NL) {
607: if (doomed >= 0)
608: doomed++;
609: goto def;
610: }
611: break;
612: }
613: }
614:
615: /*
616: * If we get a blank not in the echo area
617: * consider splitting the window in the wrapmargin.
618: */
619: if (c != NL && !splitw) {
620: if (c == ' ' && gobblebl) {
621: gobbled = 1;
622: continue;
623: }
624: if (value(WRAPMARGIN) &&
625: (outcol >= OCOLUMNS - value(WRAPMARGIN) ||
626: backsl && outcol==0) &&
627: commch != 'r') {
628: /*
629: * At end of word and hit wrapmargin.
630: * Move the word to next line and keep going.
631: */
632: wdkind = 1;
633: *gcursor++ = c;
634: if (backsl)
635: *gcursor++ = getkey();
636: *gcursor = 0;
637: /*
638: * Find end of previous word if we are past it.
639: */
640: for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--)
641: ;
642: if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
643: /*
644: * Find beginning of previous word.
645: */
646: for (; cp>ogcursor && !isspace(cp[-1]); cp--)
647: ;
648: if (cp <= ogcursor) {
649: /*
650: * There is a single word that
651: * is too long to fit. Just
652: * let it pass, but beep for
653: * each new letter to warn
654: * the luser.
655: */
656: c = *--gcursor;
657: *gcursor = 0;
658: beep();
659: goto dontbreak;
660: }
661: /*
662: * Save it for next line.
663: */
664: macpush(cp, 0);
665: cp--;
666: }
667: macpush("\n", 0);
668: /*
669: * Erase white space before the word.
670: */
671: while (cp > ogcursor && isspace(cp[-1]))
672: cp--; /* skip blank */
673: gobblebl = 3;
674: goto vbackup;
675: }
676: dontbreak:;
677: }
678:
679: /*
680: * Word abbreviation mode.
681: */
682: cstr[0] = c;
683: if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
684: int wdtype, abno;
685:
686: cstr[1] = 0;
687: wdkind = 1;
688: cp = gcursor - 1;
689: for (wdtype = wordch(cp - 1);
690: cp > ogcursor && wordof(wdtype, cp - 1); cp--)
691: ;
692: *gcursor = 0;
693: for (abno=0; abbrevs[abno].mapto; abno++) {
694: if (eq(cp, abbrevs[abno].cap)) {
695: macpush(cstr, 0);
696: macpush(abbrevs[abno].mapto);
697: goto vbackup;
698: }
699: }
700: }
701:
702: switch (c) {
703:
704: /*
705: * ^M Except in repeat maps to \n.
706: */
707: case CR:
708: if (vglobp)
709: goto def;
710: c = '\n';
711: /* presto chango ... */
712:
713: /*
714: * \n Start new line.
715: */
716: case NL:
717: *aescaped = c;
718: goto vadone;
719:
720: /*
721: * escape End insert unless repeat and more to repeat.
722: */
723: case ESCAPE:
724: if (lastvgk)
725: goto def;
726: goto vadone;
727:
728: /*
729: * ^D Backtab.
730: * ^T Software forward tab.
731: *
732: * Unless in repeat where this means these
733: * were superquoted in.
734: */
735: case CTRL(d):
736: case CTRL(t):
737: if (vglobp)
738: goto def;
739: /* fall into ... */
740:
741: /*
742: * ^D|QUOTE Is a backtab (in a repeated command).
743: */
744: case CTRL(d) | QUOTE:
745: *gcursor = 0;
746: cp = vpastwh(genbuf);
747: c = whitecnt(genbuf);
748: if (ch == CTRL(t)) {
749: /*
750: * ^t just generates new indent replacing
751: * current white space rounded up to soft
752: * tab stop increment.
753: */
754: if (cp != gcursor)
755: /*
756: * BUG: Don't hack ^T except
757: * right after initial
758: * white space.
759: */
760: continue;
761: cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
762: ogcursor = cp;
763: goto vbackup;
764: }
765: /*
766: * ^D works only if we are at the (end of) the
767: * generated autoindent. We count the ^D for repeat
768: * purposes.
769: */
770: if (c == iwhite && c != 0)
771: if (cp == gcursor) {
772: iwhite = backtab(c);
773: CDCNT++;
774: ogcursor = cp = genindent(iwhite);
775: goto vbackup;
776: } else if (&cp[1] == gcursor &&
777: (*cp == '^' || *cp == '0')) {
778: /*
779: * ^^D moves to margin, then back
780: * to current indent on next line.
781: *
782: * 0^D moves to margin and then
783: * stays there.
784: */
785: HADZERO = *cp == '0';
786: ogcursor = cp = genbuf;
787: HADUP = 1 - HADZERO;
788: CDCNT = 1;
789: endim();
790: back1();
791: vputchar(' ');
792: goto vbackup;
793: }
794: if (vglobp && vglobp - iglobp >= 2 &&
795: (vglobp[-2] == '^' || vglobp[-2] == '0')
796: && gcursor == ogcursor + 1)
797: goto bakchar;
798: continue;
799:
800: default:
801: /*
802: * Possibly discard control inputs.
803: */
804: if (!vglobp && junk(c)) {
805: beep();
806: continue;
807: }
808: def:
809: if (!backsl) {
810: int cnt;
811: putchar(c);
812: flush();
813: }
814: if (gcursor > &genbuf[LBSIZE - 2])
815: error("Line too long");
816: *gcursor++ = c & TRIM;
817: vcsync();
818: if (value(SHOWMATCH) && !iglobp)
819: if (c == ')' || c == '}')
820: lsmatch(gcursor);
821: continue;
822: }
823: }
824: vadone:
825: *gcursor = 0;
826: if (Outchar != termchar)
827: Outchar = OO;
828: endim();
829: return (gcursor);
830: }
831:
832: int vgetsplit();
833: char *vsplitpt;
834:
835: /*
836: * Append the line in buffer at lp
837: * to the buffer after dot.
838: */
839: vdoappend(lp)
840: char *lp;
841: {
842: register int oing = inglobal;
843:
844: vsplitpt = lp;
845: inglobal = 1;
846: ignore(append(vgetsplit, dot));
847: inglobal = oing;
848: }
849:
850: /*
851: * Subroutine for vdoappend to pass to append.
852: */
853: vgetsplit()
854: {
855:
856: if (vsplitpt == 0)
857: return (EOF);
858: strcLIN(vsplitpt);
859: vsplitpt = 0;
860: return (0);
861: }
862:
863: /*
864: * Vmaxrep determines the maximum repetitition factor
865: * allowed that will yield total line length less than
866: * LBSIZE characters and also does hacks for the R command.
867: */
868: vmaxrep(ch, cnt)
869: char ch;
870: register int cnt;
871: {
872: register int len, replen;
873:
874: if (cnt > LBSIZE - 2)
875: cnt = LBSIZE - 2;
876: replen = strlen(genbuf);
877: if (ch == 'R') {
878: len = strlen(cursor);
879: if (replen < len)
880: len = replen;
881: CP(cursor, cursor + len);
882: vUD2 += len;
883: }
884: len = strlen(linebuf);
885: if (len + cnt * replen <= LBSIZE - 2)
886: return (cnt);
887: cnt = (LBSIZE - 2 - len) / replen;
888: if (cnt == 0) {
889: vsave();
890: error("Line too long");
891: }
892: return (cnt);
893: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.