|
|
1.1 root 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: */
11: char *vUA1, *vUA2;
12: char *vUD1, *vUD2;
13:
14: /*
15: * Obleeperate characters in hardcopy
16: * open with \'s.
17: */
18: bleep(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: */
34: vdcMID()
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: */
52: takeout(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: */
77: ateopr()
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: */
100: bool vaifirst;
101: bool gobbled;
102: char *ogcursor;
103:
104: vappend(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: */
379: back1()
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: */
399: char *
400: vgetline(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):
485: bakchar:
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;
520: vbackup:
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: }
699: def:
700: putchar(c);
701: noput:
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: }
714: vadone:
715: *gcursor = 0;
716: Outchar = OO;
717: endim();
718: return (gcursor);
719: }
720:
721: int vgetsplit();
722: char *vsplitpt;
723:
724: /*
725: * Append the line in buffer at lp
726: * to the buffer after dot.
727: */
728: vdoappend(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: */
742: vgetsplit()
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: */
757: vmaxrep(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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.