1 /*
2 * This code contains changes by
3 * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
4 *
5 * Conditions 1, 2, and 4 and the no-warranty notice below apply
6 * to these changes.
7 *
8 *
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 *
41 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * Redistributions of source code and documentation must retain the
47 * above copyright notice, this list of conditions and the following
48 * disclaimer.
49 * Redistributions in binary form must reproduce the above copyright
50 * notice, this list of conditions and the following disclaimer in the
51 * documentation and/or other materials provided with the distribution.
52 * All advertising materials mentioning features or use of this software
53 * must display the following acknowledgement:
54 * This product includes software developed or owned by Caldera
55 * International, Inc.
56 * Neither the name of Caldera International, Inc. nor the names of
57 * other contributors may be used to endorse or promote products
58 * derived from this software without specific prior written permission.
59 *
60 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
61 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
62 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
63 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
65 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
66 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
67 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
68 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
69 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
70 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
71 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72 */
73
74 #ifndef lint
75 #ifdef DOSCCS
76 static char sccsid[] = "@(#)ex_vops2.c 1.34 (gritter) 1/12/05";
77 #endif
78 #endif
79
80 /* from ex_vops2.c 6.8 (Berkeley) 6/7/85 */
81
82 #include "ex.h"
83 #include "ex_tty.h"
84 #include "ex_vis.h"
85
86 /*
87 * Low level routines for operations sequences,
88 * and mostly, insert mode (and a subroutine
89 * to read an input line, including in the echo area.)
90 */
91 extern char *vUA1, *vUA2; /* mjm: extern; also in ex_vops.c */
92 extern char *vUD1, *vUD2; /* mjm: extern; also in ex_vops.c */
93
94 /*
95 * Obleeperate characters in hardcopy
96 * open with \'s.
97 */
98 void
bleep(register int i,char * cp)99 bleep(register int i, char *cp)
100 {
101
102 i -= column(cp);
103 do
104 putchar('\\' | QUOTE);
105 while (--i >= 0);
106 rubble = 1;
107 }
108
109 /*
110 * Common code for middle part of delete
111 * and change operating on parts of lines.
112 */
113 int
vdcMID(void)114 vdcMID(void)
115 {
116 register char *cp;
117
118 squish();
119 setLAST();
120 if (FIXUNDO)
121 vundkind = VCHNG, CP(vutmp, linebuf);
122 if (wcursor < cursor)
123 cp = wcursor, wcursor = cursor, cursor = cp;
124 vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
125 return (column(wcursor + skipleft(linebuf, wcursor)));
126 }
127
128 /*
129 * Take text from linebuf and stick it
130 * in the VBSIZE buffer BUF. Used to save
131 * deleted text of part of line.
132 */
133 void
takeout(cell * BUF)134 takeout(cell *BUF)
135 {
136 register char *cp;
137
138 if (wcursor < linebuf)
139 wcursor = linebuf;
140 if (cursor == wcursor) {
141 beep();
142 return;
143 }
144 if (wcursor < cursor) {
145 cp = wcursor;
146 wcursor = cursor;
147 cursor = cp;
148 }
149 setBUF(BUF);
150 if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
151 beep();
152 }
153
154 /*
155 * Are we at the end of the printed representation of the
156 * line? Used internally in hardcopy open.
157 */
158 int
ateopr(void)159 ateopr(void)
160 {
161 register int i, c;
162 register cell *cp = vtube[destline] + destcol;
163
164 for (i = WCOLS - destcol; i > 0; i--) {
165 c = *cp++;
166 if (c == 0)
167 return (1);
168 if (c != ' ' && (c & QUOTE) == 0)
169 return (0);
170 }
171 return (1);
172 }
173
174 void
showmode(int mode)175 showmode(int mode)
176 {
177 int sdc = destcol, sdl = destline;
178 char *ocurs, *str;
179
180 if (value(SHOWMODE) == 0 || TCOLUMNS <= 20 || state == ONEOPEN
181 || state == HARDOPEN || vmacp != NULL)
182 return;
183 ocurs = cursor;
184 fixech();
185 vgoto(WECHO, TCOLUMNS - 20);
186 switch (mode) {
187 case 0: str = catgets(catd, 1, 227,
188 " ");
189 break;
190 case 'A': /*FALLTHROUGH*/
191 case 'a': str = catgets(catd, 1, 228,
192 "AAPPEND MODE");
193 break;
194 case 'C': /*FALLTHROUGH*/
195 case 'c': str = catgets(catd, 1, 229,
196 "CCHANGE MODE");
197 break;
198 case 'O': /*FALLTHROUGH*/
199 case 'o': str = catgets(catd, 1, 230,
200 "OOPEN MODE");
201 break;
202 case 'R': str = catgets(catd, 1, 231,
203 "RREPLACE MODE");
204 break;
205 case 'r': str = catgets(catd, 1, 232,
206 "rREPLACE 1 CHAR");
207 break;
208 default: str = catgets(catd, 1, 233,
209 "IINSERT MODE");
210 }
211 if (value(TERSE))
212 putchar(str[0]);
213 else
214 printf(&str[1]);
215 vgoto(sdl, sdc);
216 cursor = ocurs;
217 splitw = 0;
218 }
219
220 /*
221 * Append.
222 *
223 * This routine handles the top level append, doing work
224 * as each new line comes in, and arranging repeatability.
225 * It also handles append with repeat counts, and calculation
226 * of autoindents for new lines.
227 */
228 bool vaifirst;
229 bool gobbled;
230 char *ogcursor;
231
232 /*
233 * The addtext() and addto() routines combined, accepting a single
234 * cell character.
235 */
236 void
addc(cell c)237 addc(cell c)
238 {
239 register cell *cp = INS;
240
241 if (vglobp)
242 return;
243 if ((cp[0] & (QUOTE|TRIM)) != OVERBUF) {
244 if (cellen(cp) + 2 >= VBSIZE) {
245 cp[0] = OVERBUF;
246 lastcmd[0] = 0;
247 } else {
248 while (*cp)
249 cp++;
250 *cp++ = c;
251 *cp++ = 0;
252 }
253 }
254 }
255
256 void
vappend(int ch,int cnt,int indent)257 vappend(int ch, int cnt, int indent)
258 /* int ch; /\* mjm: char --> int */
259 {
260 register int i = 0;
261 register char *gcursor;
262 bool escape;
263 int repcnt, savedoomed;
264 short oldhold = hold;
265 #ifdef SIGWINCH
266 sigset_t set, oset;
267 #endif
268
269 /*
270 * Before a move in hardopen when the line is dirty
271 * or we are in the middle of the printed representation,
272 * we retype the line to the left of the cursor so the
273 * insert looks clean.
274 */
275 if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
276 rubble = 1;
277 gcursor = cursor;
278 i = *gcursor;
279 *gcursor = ' ';
280 wcursor = gcursor;
281 vmove(0);
282 *gcursor = i;
283 }
284 vaifirst = indent == 0;
285
286 showmode(ch);
287
288 /*
289 * Handle replace character by (eventually)
290 * limiting the number of input characters allowed
291 * in the vgetline routine.
292 */
293 if (ch == 'r')
294 repcnt = 2;
295 else
296 repcnt = 0;
297
298 /*
299 * If an autoindent is specified, then
300 * generate a mixture of blanks to tabs to implement
301 * it and place the cursor after the indent.
302 * Text read by the vgetline routine will be placed in genbuf,
303 * so the indent is generated there.
304 */
305 if (value(AUTOINDENT) && indent != 0) {
306 gcursor = genindent(indent);
307 *gcursor = 0;
308 vgotoCL(qcolumn(cursor + skipright(cursor, linebuf), genbuf));
309 } else {
310 gcursor = genbuf;
311 *gcursor = 0;
312 if (ch == 'o')
313 vfixcurs();
314 }
315
316 /*
317 * Prepare for undo. Pointers delimit inserted portion of line.
318 */
319 vUA1 = vUA2 = cursor;
320
321 /*
322 * If we are not in a repeated command and a ^@ comes in
323 * then this means the previous inserted text.
324 * If there is none or it was too long to be saved,
325 * then beep() and also arrange to undo any damage done
326 * so far (e.g. if we are a change.)
327 */
328 if ((vglobp && *vglobp == 0) || peekbr()) {
329 if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
330 beep();
331 if (!splitw)
332 ungetkey('u');
333 doomed = 0;
334 hold = oldhold;
335 showmode(0);
336 return;
337 }
338 /*
339 * Unread input from INS.
340 * An escape will be generated at end of string.
341 * Hold off n^^2 type update on dumb terminals.
342 */
343 vglobp = INS;
344 hold |= HOLDQIK;
345 } else if (vglobp == 0)
346 /*
347 * Not a repeated command, get
348 * a new inserted text for repeat.
349 */
350 INS[0] = 0;
351
352 /*
353 * For wrapmargin to hack away second space after a '.'
354 * when the first space caused a line break we keep
355 * track that this happened in gobblebl, which says
356 * to gobble up a blank silently.
357 */
358 gobblebl = 0;
359
360 #ifdef SIGWINCH
361 sigemptyset(&set);
362 sigaddset(&set, SIGWINCH);
363 sigprocmask(SIG_BLOCK, &set, &oset);
364 #endif
365 /*
366 * Text gathering loop.
367 * New text goes into genbuf starting at gcursor.
368 * cursor preserves place in linebuf where text will eventually go.
369 */
370 if (*cursor == 0 || state == CRTOPEN)
371 hold |= HOLDROL;
372 for (;;) {
373 if (ch == 'r' && repcnt == 0)
374 escape = 0;
375 else {
376 gcursor = vgetline(repcnt, gcursor, &escape, ch);
377
378 /*
379 * After an append, stick information
380 * about the ^D's and ^^D's and 0^D's in
381 * the repeated text buffer so repeated
382 * inserts of stuff indented with ^D as backtab's
383 * can work.
384 */
385 if (HADUP)
386 addc('^');
387 else if (HADZERO)
388 addc('0');
389 while (CDCNT > 0)
390 #ifndef BIT8
391 addc('\204'), CDCNT--;
392 #else
393 addc(OVERBUF), CDCNT--;
394 #endif
395
396 if (gobbled)
397 addc(' ');
398 addtext(ogcursor);
399 }
400 repcnt = 0;
401
402 /*
403 * Smash the generated and preexisting indents together
404 * and generate one cleanly made out of tabs and spaces
405 * if we are using autoindent.
406 */
407 if (!vaifirst && value(AUTOINDENT)) {
408 i = fixindent(indent);
409 if (!HADUP)
410 indent = i;
411 gcursor = strend(genbuf);
412 }
413
414 /*
415 * Limit the repetition count based on maximum
416 * possible line length; do output implied
417 * by further count (> 1) and cons up the new line
418 * in linebuf.
419 */
420 cnt = vmaxrep(ch, cnt);
421 CP(gcursor + skipright(ogcursor, gcursor), cursor);
422 do {
423 CP(cursor, genbuf);
424 if (cnt > 1) {
425 int oldhold = hold;
426
427 Outchar = vinschar;
428 hold |= HOLDQIK;
429 printf("%s", genbuf);
430 hold = oldhold;
431 Outchar = vputchar;
432 }
433 cursor += gcursor - genbuf;
434 } while (--cnt > 0);
435 endim();
436 vUA2 = cursor;
437 if (escape != '\n')
438 CP(cursor, gcursor + skipright(ogcursor, gcursor));
439
440 /*
441 * If doomed characters remain, clobber them,
442 * and reopen the line to get the display exact.
443 */
444 if (state != HARDOPEN) {
445 DEPTH(vcline) = 0;
446 savedoomed = doomed;
447 if (doomed > 0) {
448 register int cind = cindent();
449
450 physdc(cind, cind + doomed);
451 doomed = 0;
452 }
453 i = vreopen(LINE(vcline), lineDOT(), vcline);
454 #ifdef TRACE
455 if (trace)
456 fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
457 #endif
458 if (ch == 'R')
459 doomed = savedoomed;
460 }
461
462 /*
463 * All done unless we are continuing on to another line.
464 */
465 if (escape != '\n')
466 break;
467
468 /*
469 * Set up for the new line.
470 * First save the current line, then construct a new
471 * first image for the continuation line consisting
472 * of any new autoindent plus the pushed ahead text.
473 */
474 showmode(0);
475 killU();
476 addc(gobblebl ? ' ' : '\n');
477 vsave();
478 cnt = 1;
479 if (value(AUTOINDENT)) {
480 #ifdef LISPCODE
481 if (value(LISP))
482 indent = lindent(dot + 1);
483 else
484 #endif
485 if (!HADUP && vaifirst)
486 indent = whitecnt(linebuf);
487 vaifirst = 0;
488 strcLIN(vpastwh(gcursor + 1));
489 gcursor = genindent(indent);
490 *gcursor = 0;
491 if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
492 gcursor = genbuf;
493 CP(gcursor, linebuf);
494 } else {
495 CP(genbuf, gcursor + skipright(ogcursor, gcursor));
496 gcursor = genbuf;
497 }
498
499 /*
500 * If we started out as a single line operation and are now
501 * turning into a multi-line change, then we had better yank
502 * out dot before it changes so that undo will work
503 * correctly later.
504 */
505 if (FIXUNDO && vundkind == VCHNG) {
506 vremote(1, yank, 0);
507 undap1--;
508 }
509
510 /*
511 * Now do the append of the new line in the buffer,
512 * and update the display. If slowopen
513 * we don't do very much.
514 */
515 vdoappend(genbuf);
516 vundkind = VMANYINS;
517 vcline++;
518 if (state != VISUAL)
519 vshow(dot, NOLINE);
520 else {
521 i += LINE(vcline - 1);
522 vopen(dot, i);
523 if (value(SLOWOPEN))
524 vscrap();
525 else
526 vsync1(LINE(vcline));
527 }
528 strcLIN(gcursor);
529 *gcursor = 0;
530 cursor = linebuf;
531 vgotoCL(qcolumn(cursor + skipleft(ogcursor, cursor), genbuf));
532 showmode(ch);
533 }
534
535 /*
536 * All done with insertion, position the cursor
537 * and sync the screen.
538 */
539 showmode(0);
540 hold = oldhold;
541 if (cursor > linebuf)
542 cursor += skipleft(linebuf, cursor);
543 if (state != HARDOPEN)
544 vsyncCL();
545 else if (cursor > linebuf)
546 back1();
547 doomed = 0;
548 wcursor = cursor;
549 vmove(0);
550 #ifdef SIGWINCH
551 sigprocmask(SIG_SETMASK, &oset, NULL);
552 #endif
553 }
554
555 /*
556 * Subroutine for vgetline to back up a single character position,
557 * backwards around end of lines (vgoto can't hack columns which are
558 * less than 0 in general).
559 */
560 void
back1(void)561 back1(void)
562 {
563
564 vgoto(destline - 1, WCOLS + destcol - 1);
565 }
566
567 #define gappend(c) { \
568 int _c = c; \
569 xgappend(_c, &gcursor); \
570 }
571
572 static void
xgappend(int c,char ** gp)573 xgappend(int c, char **gp)
574 {
575 if (*gp >= &genbuf[MAXBSIZE-mb_cur_max-1]) {
576 beep();
577 return;
578 }
579 #ifdef MB
580 if (mb_cur_max > 1 && !(c & INVBIT)) {
581 char mb[MB_LEN_MAX];
582 int i, n;
583 n = wctomb(mb, c);
584 for (i = 0; i < n; i++)
585 *(*gp)++ = mb[i];
586 } else
587 #endif /* MB */
588 *(*gp)++ = c & 0377;
589 }
590
591 /*
592 * Get a line into genbuf after gcursor.
593 * Cnt limits the number of input characters
594 * accepted and is used for handling the replace
595 * single character command. Aescaped is the location
596 * where we stick a termination indicator (whether we
597 * ended with an ESCAPE or a newline/return.
598 *
599 * We do erase-kill type processing here and also
600 * are careful about the way we do this so that it is
601 * repeatable. (I.e. so that your kill doesn't happen,
602 * when you repeat an insert if it was escaped with \ the
603 * first time you did it. commch is the command character
604 * involved, including the prompt for readline.
605 */
606 char *
vgetline(int cnt,char * gcursor,bool * aescaped,int commch)607 vgetline(int cnt, char *gcursor, bool *aescaped, int commch)
608 {
609 register int c, ch;
610 register char *cp;
611 int x, y, iwhite, backsl=0;
612 cell *iglobp;
613 char cstr[2];
614 int (*OO)() = Outchar;
615
616 /*
617 * Clear the output state and counters
618 * for autoindent backwards motion (counts of ^D, etc.)
619 * Remember how much white space at beginning of line so
620 * as not to allow backspace over autoindent.
621 */
622 *aescaped = 0;
623 ogcursor = gcursor;
624 flusho();
625 CDCNT = 0;
626 HADUP = 0;
627 HADZERO = 0;
628 gobbled = 0;
629 iwhite = whitecnt(genbuf);
630 iglobp = vglobp;
631
632 /*
633 * Carefully avoid using vinschar in the echo area.
634 */
635 if (splitw)
636 Outchar = vputchar;
637 else {
638 Outchar = vinschar;
639 vprepins();
640 }
641 for (;;) {
642 backsl = 0;
643 if (gobblebl)
644 gobblebl--;
645 if (cnt != 0) {
646 cnt--;
647 if (cnt == 0)
648 goto vadone;
649 }
650 c = getkey();
651 if (c != ATTN)
652 c &= (QUOTE|TRIM);
653 ch = c;
654 maphopcnt = 0;
655 if (vglobp == 0 && Peekkey == 0 && commch != 'r')
656 while ((ch = map(c, immacs)) != c) {
657 c = ch;
658 if (!value(REMAP))
659 break;
660 if (++maphopcnt > 256)
661 error(catgets(catd, 1, 234,
662 "Infinite macro loop"));
663 }
664 if (!iglobp) {
665
666 /*
667 * Erase-kill type processing.
668 * Only happens if we were not reading
669 * from untyped input when we started.
670 * Map users erase to ^H, kill to -1 for switch.
671 */
672 if (c == tty.c_cc[VERASE])
673 c = CTRL('h');
674 else if (c == tty.c_cc[VKILL])
675 c = -1;
676 if (c == ATTN)
677 goto case_ATTN;
678 switch (c) {
679
680 /*
681 * ^? Interrupt drops you back to visual
682 * command mode with an unread interrupt
683 * still in the input buffer.
684 *
685 * ^\ Quit does the same as interrupt.
686 * If you are a ex command rather than
687 * a vi command this will drop you
688 * back to command mode for sure.
689 */
690 case QUIT:
691 case_ATTN:
692 ungetkey(c);
693 goto vadone;
694
695 /*
696 * ^H Backs up a character in the input.
697 *
698 * BUG: Can't back around line boundaries.
699 * This is hard because stuff has
700 * already been saved for repeat.
701 */
702 case CTRL('h'):
703 bakchar:
704 cp = gcursor + skipleft(ogcursor, gcursor);
705 if (cp < ogcursor) {
706 if (splitw) {
707 /*
708 * Backspacing over readecho
709 * prompt. Pretend delete but
710 * don't beep.
711 */
712 ungetkey(c);
713 goto vadone;
714 }
715 beep();
716 continue;
717 }
718 goto vbackup;
719
720 /*
721 * ^W Back up a white/non-white word.
722 */
723 case CTRL('w'):
724 wdkind = 1;
725 for (cp = gcursor; cp > ogcursor
726 && isspace(cp[-1]&0377); cp--)
727 continue;
728 for (c = wordch(cp - 1);
729 cp > ogcursor && wordof(c, cp - 1); cp--)
730 continue;
731 goto vbackup;
732
733 /*
734 * users kill Kill input on this line, back to
735 * the autoindent.
736 */
737 case -1:
738 cp = ogcursor;
739 vbackup:
740 if (cp == gcursor) {
741 beep();
742 continue;
743 }
744 endim();
745 *cp = 0;
746 c = cindent();
747 vgotoCL(qcolumn(cursor +
748 skipleft(linebuf, cursor), genbuf));
749 if (doomed >= 0)
750 doomed += c - cindent();
751 gcursor = cp;
752 continue;
753
754 /*
755 * \ Followed by erase or kill
756 * maps to just the erase or kill.
757 */
758 case '\\':
759 x = destcol, y = destline;
760 putchar('\\');
761 vcsync();
762 c = getkey();
763 if (c == tty.c_cc[VERASE]
764 || c == tty.c_cc[VKILL])
765 {
766 vgoto(y, x);
767 if (doomed >= 0)
768 doomed++;
769 goto def;
770 }
771 ungetkey(c), c = '\\';
772 backsl = 1;
773 break;
774
775 /*
776 * ^Q Super quote following character
777 * Only ^@ is verboten (trapped at
778 * a lower level) and \n forces a line
779 * split so doesn't really go in.
780 *
781 * ^V Synonym for ^Q
782 */
783 case CTRL('q'):
784 case CTRL('v'):
785 x = destcol, y = destline;
786 putchar('^');
787 vgoto(y, x);
788 c = getkey();
789 if (c != NL) {
790 if (doomed >= 0)
791 doomed++;
792 goto def;
793 }
794 break;
795 }
796 }
797
798 /*
799 * If we get a blank not in the echo area
800 * consider splitting the window in the wrapmargin.
801 */
802 if (c != NL && !splitw) {
803 if (c == ' ' && gobblebl) {
804 gobbled = 1;
805 continue;
806 }
807 if (value(WRAPMARGIN) &&
808 (outcol >= OCOLUMNS - value(WRAPMARGIN) ||
809 backsl && outcol==0) &&
810 commch != 'r') {
811 /*
812 * At end of word and hit wrapmargin.
813 * Move the word to next line and keep going.
814 */
815 wdkind = 1;
816 gappend(c);
817 if (backsl)
818 gappend(getkey());
819 *gcursor = 0;
820 /*
821 * Find end of previous word if we are past it.
822 */
823 for (cp=gcursor; cp>ogcursor
824 && isspace(cp[-1]&0377); cp--)
825 ;
826 if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
827 /*
828 * Find beginning of previous word.
829 */
830 for (; cp>ogcursor && !isspace(cp[-1]&0377); cp--)
831 ;
832 if (cp <= ogcursor) {
833 /*
834 * There is a single word that
835 * is too long to fit. Just
836 * let it pass, but beep for
837 * each new letter to warn
838 * the luser.
839 */
840 c = *--gcursor;
841 *gcursor = 0;
842 beep();
843 goto dontbreak;
844 }
845 /*
846 * Save it for next line.
847 */
848 macpush(cp, 0);
849 cp--;
850 }
851 macpush("\n", 0);
852 /*
853 * Erase white space before the word.
854 */
855 while (cp > ogcursor && isspace(cp[-1]&0377))
856 cp--; /* skip blank */
857 gobblebl = 3;
858 goto vbackup;
859 }
860 dontbreak:;
861 }
862
863 /*
864 * Word abbreviation mode.
865 */
866 cstr[0] = c;
867 if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
868 int wdtype, abno;
869
870 cstr[1] = 0;
871 wdkind = 1;
872 cp = gcursor + skipleft(ogcursor, gcursor);
873 for (wdtype = wordch(cp - 1);
874 cp > ogcursor && wordof(wdtype, cp - 1); cp--)
875 ;
876 *gcursor = 0;
877 for (abno=0; abbrevs[abno].mapto; abno++) {
878 if (!abbrevs[abno].hadthis &&
879 eq(cp, abbrevs[abno].cap)) {
880 abbrevs[abno].hadthis++;
881 macpush(cstr, 0);
882 macpush(abbrevs[abno].mapto, 0);
883 goto vbackup;
884 }
885 }
886 }
887
888 #ifdef BIT8
889 if (c == OVERBUF)
890 goto btrp;
891 #endif
892 switch (c) {
893
894 /*
895 * ^M Except in repeat maps to \n.
896 */
897 case CR:
898 if (vglobp)
899 goto def;
900 c = '\n';
901 /* presto chango ... */
902
903 /*
904 * \n Start new line.
905 */
906 case NL:
907 *aescaped = c;
908 goto vadone;
909
910 /*
911 * escape End insert unless repeat and more to repeat.
912 */
913 case ESCAPE:
914 if (lastvgk)
915 goto def;
916 goto vadone;
917
918 /*
919 * ^D Backtab.
920 * ^T Software forward tab.
921 *
922 * Unless in repeat where this means these
923 * were superquoted in.
924 */
925 case CTRL('d'):
926 case CTRL('t'):
927 if (vglobp)
928 goto def;
929 /* fall into ... */
930
931 /*
932 * ^D|QUOTE Is a backtab (in a repeated command).
933 */
934 #ifndef BIT8
935 case CTRL('d') | QUOTE:
936 #else
937 btrp:
938 #endif
939 *gcursor = 0;
940 cp = vpastwh(genbuf);
941 c = whitecnt(genbuf);
942 if (ch == CTRL('t')) {
943 /*
944 * ^t just generates new indent replacing
945 * current white space rounded up to soft
946 * tab stop increment.
947 */
948 if (cp != gcursor)
949 /*
950 * BUG: Don't hack ^T except
951 * right after initial
952 * white space.
953 */
954 continue;
955 cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
956 ogcursor = cp;
957 goto vbackup;
958 }
959 /*
960 * ^D works only if we are at the (end of) the
961 * generated autoindent. We count the ^D for repeat
962 * purposes.
963 */
964 if (c == iwhite && c != 0)
965 if (cp == gcursor) {
966 iwhite = backtab(c);
967 CDCNT++;
968 ogcursor = cp = genindent(iwhite);
969 goto vbackup;
970 } else if (&cp[1] == gcursor &&
971 (*cp == '^' || *cp == '0')) {
972 /*
973 * ^^D moves to margin, then back
974 * to current indent on next line.
975 *
976 * 0^D moves to margin and then
977 * stays there.
978 */
979 HADZERO = *cp == '0';
980 ogcursor = cp = genbuf;
981 HADUP = 1 - HADZERO;
982 CDCNT = 1;
983 endim();
984 back1();
985 vputchar(' ');
986 goto vbackup;
987 }
988 if (vglobp && vglobp - iglobp >= 2 &&
989 (vglobp[-2] == '^' || vglobp[-2] == '0')
990 && gcursor == ogcursor + 1)
991 goto bakchar;
992 continue;
993
994 default:
995 /*
996 * Possibly discard control inputs.
997 */
998 if (!vglobp && junk(c)) {
999 beep();
1000 continue;
1001 }
1002 def:
1003 if (!backsl) {
1004 /* int cnt; */
1005 putchar(c);
1006 flush();
1007 }
1008 if (gcursor > &genbuf[LBSIZE - 2])
1009 error(catgets(catd, 1, 235, "Line too long"));
1010 gappend(c & TRIM);
1011 vcsync();
1012 if (value(SHOWMATCH) && !iglobp)
1013 if (c == ')' || c == '}')
1014 lsmatch(gcursor);
1015 continue;
1016 }
1017 }
1018 vadone:
1019 *gcursor = 0;
1020 if (Outchar != termchar)
1021 Outchar = OO;
1022 endim();
1023 return (gcursor);
1024 }
1025
1026 char *vsplitpt;
1027
1028 /*
1029 * Append the line in buffer at lp
1030 * to the buffer after dot.
1031 */
1032 void
vdoappend(char * lp)1033 vdoappend(char *lp)
1034 {
1035 register int oing = inglobal;
1036
1037 vsplitpt = lp;
1038 inglobal = 1;
1039 ignore(append(vgetsplit, dot));
1040 inglobal = oing;
1041 }
1042
1043 /*
1044 * Subroutine for vdoappend to pass to append.
1045 */
1046 int
vgetsplit(void)1047 vgetsplit(void)
1048 {
1049
1050 if (vsplitpt == 0)
1051 return (EOF);
1052 strcLIN(vsplitpt);
1053 vsplitpt = 0;
1054 return (0);
1055 }
1056
1057 /*
1058 * Vmaxrep determines the maximum repetitition factor
1059 * allowed that will yield total line length less than
1060 * LBSIZE characters and also does hacks for the R command.
1061 */
1062 int
vmaxrep(int ch,register int cnt)1063 vmaxrep(int ch, register int cnt)
1064 {
1065 register int len, replen;
1066
1067 if (cnt > LBSIZE - 2)
1068 cnt = LBSIZE - 2;
1069 replen = strlen(genbuf);
1070 if (ch == 'R') {
1071 len = strlen(cursor);
1072 if (replen < len)
1073 len = replen;
1074 #ifdef MB
1075 if (mb_cur_max > 1) {
1076 char *cp, *gp;
1077 int c, g;
1078 for (gp = genbuf, g = 0; *gp; g++)
1079 gp += wskipright(genbuf, gp);
1080 for (cp = cursor, c = 0; c < g; c++)
1081 cp += wskipright(cursor, cp);
1082 CP(cursor, cp);
1083 } else
1084 #endif /* MB */
1085 CP(cursor, cursor + len);
1086 vUD2 += len;
1087 }
1088 len = strlen(linebuf);
1089 if (len + cnt * replen <= LBSIZE - 2)
1090 return (cnt);
1091 cnt = (LBSIZE - 2 - len) / replen;
1092 if (cnt == 0) {
1093 vsave();
1094 error(catgets(catd, 1, 236, "Line too long"));
1095 }
1096 return (cnt);
1097 }
1098