1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29
30 /* Copyright (c) 1981 Regents of the University of California */
31
32 #pragma ident "%Z%%M% %I% %E% SMI"
33
34 #include "ex.h"
35 #include "ex_tty.h"
36 #include "ex_vis.h"
37 #ifndef PRESUNEUC
38 #include <wctype.h>
39 /* Undef putchar/getchar if they're defined. */
40 #ifdef putchar
41 # undef putchar
42 #endif
43 #ifdef getchar
44 # undef getchar
45 #endif
46 #endif /* PRESUNEUC */
47
48 /*
49 * This is the main routine for visual.
50 * We here decode the count and possible named buffer specification
51 * preceding a command and interpret a few of the commands.
52 * Commands which involve a target (i.e. an operator) are decoded
53 * in the routine operate in ex_voperate.c.
54 */
55
56 #define forbid(a) { if (a) goto fonfon; }
57
58 extern int windowchg;
59 extern int sigok;
60 #ifdef XPG6
61 int redisplay; /* XPG6 assertion 313 [count]r\n : Also used in ex_vops2.c */
62 #endif
63 void redraw(), windowinit();
64
65 #ifdef XPG4
66 extern int P_cursor_offset;
67 #endif
68
69 void
vmain(void)70 vmain(void)
71 {
72 int c, cnt, i;
73 wchar_t esave[TUBECOLS];
74 extern wchar_t atube[];
75 unsigned char *oglobp;
76 short d;
77 line *addr;
78 int ind, nlput;
79 int shouldpo = 0;
80 int tag_reset_wrap = 0;
81 int onumber, olist, (*OPline)(), (*OPutchar)();
82
83
84 vch_mac = VC_NOTINMAC;
85 ixlatctl(0);
86
87 /*
88 * If we started as a vi command (on the command line)
89 * then go process initial commands (recover, next or tag).
90 */
91 if (initev) {
92 oglobp = globp;
93 globp = initev;
94 hadcnt = cnt = 0;
95 i = tchng;
96 addr = dot;
97 goto doinit;
98 }
99
100 vshowmode(""); /* As a precaution */
101 /*
102 * NB:
103 *
104 * The current line is always in the line buffer linebuf,
105 * and the cursor at the position cursor. You should do
106 * a vsave() before moving off the line to make sure the disk
107 * copy is updated if it has changed, and a getDOT() to get
108 * the line back if you mung linebuf. The motion
109 * routines in ex_vwind.c handle most of this.
110 */
111 for (;;) {
112 /*
113 * Decode a visual command.
114 * First sync the temp file if there has been a reasonable
115 * amount of change. Clear state for decoding of next
116 * command.
117 */
118 TSYNC();
119 vglobp = 0;
120 vreg = 0;
121 hold = 0;
122 seenprompt = 1;
123 wcursor = 0;
124 Xhadcnt = hadcnt = 0;
125 Xcnt = cnt = 1;
126 splitw = 0;
127 if (i = holdupd && !windowchg) {
128 if (state == VISUAL) {
129 sigok = 1;
130 (void)peekkey();
131 sigok = 0;
132 }
133
134 holdupd = 0;
135 /*
136 if (LINE(0) < ZERO) {
137 vclear();
138 vcnt = 0;
139 i = 3;
140 }
141 */
142 if (state != VISUAL) {
143 vcnt = 0;
144 vsave();
145 vrepaint(cursor);
146 } else if (i == 3)
147 vredraw(WTOP);
148 else
149 vsync(WTOP);
150 vfixcurs();
151 } else if(windowchg)
152 redraw();
153
154 #ifdef XPG6
155 if (redisplay) {
156 /* XPG6 assertion 313 & 254 : after [count]r\n */
157 fixdisplay();
158 }
159 redisplay = 0;
160 #endif
161 /*
162 * Gobble up counts and named buffer specifications.
163 */
164 for (;;) {
165 looptop:
166 #ifdef MDEBUG
167 if (trace)
168 fprintf(trace, "pc=%c",peekkey());
169 #endif
170 sigok = 1;
171 c = peekkey();
172 sigok = 0;
173 if (isdigit(peekkey()) && peekkey() != '0') {
174 hadcnt = 1;
175 cnt = vgetcnt();
176 forbid (cnt <= 0);
177 }
178 if (peekkey() != '"')
179 break;
180 (void)getkey(), c = getkey();
181 /*
182 * Buffer names be letters or digits.
183 * But not '0' as that is the source of
184 * an 'empty' named buffer spec in the routine
185 * kshift (see ex_temp.c).
186 */
187 if(!isascii(c) && MB_CUR_MAX > 1) {
188 /* get rest of character */
189 wchar_t wchar;
190 char multic[MULTI_BYTE_MAX];
191 ungetkey(c);
192 (void)_mbftowc(multic, &wchar, getkey, &Peekkey);
193 }
194 forbid (c == '0' || !isalpha(c) && !isascii(c) && !isdigit(c));
195 vreg = c;
196 }
197 reread:
198 /*
199 * Come to reread from below after some macro expansions.
200 * The call to map allows use of function key pads
201 * by performing a terminal dependent mapping of inputs.
202 */
203 #ifdef MDEBUG
204 if (trace)
205 fprintf(trace,"pcb=%c,",peekkey());
206 #endif
207 op = getkey();
208 maphopcnt = 0;
209 do {
210 /*
211 * Keep mapping the char as long as it changes.
212 * This allows for double mappings, e.g., q to #,
213 * #1 to something else.
214 */
215 c = op;
216 op = map(c, arrows, 0);
217 #ifdef MDEBUG
218 if (trace)
219 fprintf(trace,"pca=%c,",c);
220 #endif
221 /*
222 * Maybe the mapped to char is a count. If so, we have
223 * to go back to the "for" to interpret it. Likewise
224 * for a buffer name.
225 */
226 if ((isdigit(c) && c!='0') || c == '"') {
227 ungetkey(c);
228 goto looptop;
229 }
230 if (!value(vi_REMAP)) {
231 c = op;
232 break;
233 }
234 if (++maphopcnt > 256)
235 error(gettext("Infinite macro loop"));
236 } while (c != op);
237
238 /*
239 * Begin to build an image of this command for possible
240 * later repeat in the buffer workcmd. It will be copied
241 * to lastcmd by the routine setLAST
242 * if/when completely specified.
243 */
244 lastcp = workcmd;
245 if (!vglobp)
246 *lastcp++ = c;
247
248 /*
249 * First level command decode.
250 */
251 switch (c) {
252
253 /*
254 * ^L Clear screen e.g. after transmission error.
255 */
256
257 /*
258 * ^R Retype screen, getting rid of @ lines.
259 * If in open, equivalent to ^L.
260 * On terminals where the right arrow key sends
261 * ^L we make ^R act like ^L, since there is no
262 * way to get ^L. These terminals (adm31, tvi)
263 * are intelligent so ^R is useless. Soroc
264 * will probably foul this up, but nobody has
265 * one of them.
266 */
267 case CTRL('l'):
268 case CTRL('r'):
269 if (c == CTRL('l') || (key_right && *key_right==CTRL('l'))) {
270 vclear();
271 vdirty(0, vcnt);
272 }
273 if (state != VISUAL) {
274 /*
275 * Get a clean line, throw away the
276 * memory of what is displayed now,
277 * and move back onto the current line.
278 */
279 vclean();
280 vcnt = 0;
281 vmoveto(dot, cursor, 0);
282 continue;
283 }
284 vredraw(WTOP);
285 /*
286 * Weird glitch -- when we enter visual
287 * in a very small window we may end up with
288 * no lines on the screen because the line
289 * at the top is too long. This forces the screen
290 * to be expanded to make room for it (after
291 * we have printed @'s ick showing we goofed).
292 */
293 if (vcnt == 0)
294 vrepaint(cursor);
295 vfixcurs();
296 continue;
297
298 /*
299 * $ Escape just cancels the current command
300 * with a little feedback.
301 */
302 case ESCAPE:
303 (void) beep();
304 continue;
305
306 /*
307 * @ Macros. Bring in the macro and put it
308 * in vmacbuf, point vglobp there and punt.
309 */
310 case '@':
311 c = getesc();
312 if (c == 0)
313 continue;
314 if (c == '@')
315 c = lastmac;
316 if (isupper(c))
317 c = tolower(c);
318 forbid(!islower(c));
319 lastmac = c;
320 vsave();
321 CATCH
322 unsigned char tmpbuf[BUFSIZE];
323
324 regbuf(c, tmpbuf, sizeof (vmacbuf));
325 macpush(tmpbuf, 1);
326 ONERR
327 lastmac = 0;
328 splitw = 0;
329 getDOT();
330 vrepaint(cursor);
331 continue;
332 ENDCATCH
333 vmacp = vmacbuf;
334 goto reread;
335
336 /*
337 * . Repeat the last (modifying) open/visual command.
338 */
339 case '.':
340 /*
341 * Check that there was a last command, and
342 * take its count and named buffer unless they
343 * were given anew. Special case if last command
344 * referenced a numeric named buffer -- increment
345 * the number and go to a named buffer again.
346 * This allows a sequence like "1pu.u.u...
347 * to successively look for stuff in the kill chain
348 * much as one does in EMACS with C-Y and M-Y.
349 */
350 forbid (lastcmd[0] == 0);
351 if (hadcnt)
352 lastcnt = cnt;
353 if (vreg)
354 lastreg = vreg;
355 else if (isdigit(lastreg) && lastreg < '9')
356 lastreg++;
357 vreg = lastreg;
358 cnt = lastcnt;
359 hadcnt = lasthad;
360 vglobp = lastcmd;
361 goto reread;
362
363 /*
364 * ^U Scroll up. A count sticks around for
365 * future scrolls as the scroll amount.
366 * Attempt to hold the indentation from the
367 * top of the screen (in logical lines).
368 *
369 * BUG: A ^U near the bottom of the screen
370 * on a dumb terminal (which can't roll back)
371 * causes the screen to be cleared and then
372 * redrawn almost as it was. In this case
373 * one should simply move the cursor.
374 */
375 case CTRL('u'):
376 if (hadcnt)
377 vSCROLL = cnt;
378 cnt = vSCROLL;
379 if (state == VISUAL)
380 ind = vcline, cnt += ind;
381 else
382 ind = 0;
383 vmoving = 0;
384 vup(cnt, ind, 1);
385 vnline((unsigned char *)NOSTR);
386 continue;
387
388 /*
389 * ^D Scroll down. Like scroll up.
390 */
391 case CTRL('d'):
392 #ifdef TRACE
393 if (trace)
394 fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
395 #endif
396 if (hadcnt)
397 vSCROLL = cnt;
398 cnt = vSCROLL;
399 if (state == VISUAL)
400 ind = vcnt - vcline - 1, cnt += ind;
401 else
402 ind = 0;
403 vmoving = 0;
404 vdown(cnt, ind, 1);
405 #ifdef TRACE
406 if (trace)
407 fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
408 #endif
409 vnline((unsigned char *)NOSTR);
410 #ifdef TRACE
411 if (trace)
412 fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
413 #endif
414 continue;
415
416 /*
417 * ^E Glitch the screen down (one) line.
418 * Cursor left on same line in file.
419 */
420 case CTRL('e'):
421 if (state != VISUAL)
422 continue;
423 if (!hadcnt)
424 cnt = 1;
425 /* Bottom line of file already on screen */
426 forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
427 ind = vcnt - vcline - 1 + cnt;
428 vdown(ind, ind, 1);
429 vnline(cursor);
430 continue;
431
432 /*
433 * ^Y Like ^E but up
434 */
435 case CTRL('y'):
436 if (state != VISUAL)
437 continue;
438 if (!hadcnt)
439 cnt = 1;
440 forbid(lineDOT()-1<=vcline); /* line 1 already there */
441 ind = vcline + cnt;
442 vup(ind, ind, 1);
443 vnline(cursor);
444 continue;
445
446
447 /*
448 * m Mark position in mark register given
449 * by following letter. Return is
450 * accomplished via ' or `; former
451 * to beginning of line where mark
452 * was set, latter to column where marked.
453 */
454 case 'm':
455 /*
456 * Getesc is generally used when a character
457 * is read as a latter part of a command
458 * to allow one to hit rubout/escape to cancel
459 * what you have typed so far. These characters
460 * are mapped to 0 by the subroutine.
461 */
462 c = getesc();
463 if (c == 0)
464 continue;
465
466 /*
467 * Markreg checks that argument is a letter
468 * and also maps ' and ` to the end of the range
469 * to allow '' or `` to reference the previous
470 * context mark.
471 */
472 c = markreg(c);
473 forbid (c == 0);
474 vsave();
475 names[c - 'a'] = (*dot &~ 01);
476 ncols[c - 'a'] = cursor;
477 anymarks = 1;
478 continue;
479
480 /*
481 * ^F Window forwards, with 2 lines of continuity.
482 * Count repeats.
483 */
484 case CTRL('f'):
485 vsave();
486 if (vcnt > 2) {
487 addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
488 forbid(addr > dol);
489 dot = addr;
490 vcnt = vcline = 0;
491 }
492 vzop(0, 0, '+');
493 continue;
494
495 /*
496 * ^B Window backwards, with 2 lines of continuity.
497 * Inverse of ^F.
498 */
499 case CTRL('b'):
500 vsave();
501 if (one + vcline != dot && vcnt > 2) {
502 addr = dot - vcline + 2 - (cnt-1)*basWLINES;
503 forbid (addr <= zero);
504 dot = addr;
505 vcnt = vcline = 0;
506 }
507 vzop(0, 0, '^');
508 continue;
509
510 /*
511 * z Screen adjustment, taking a following character:
512 * zcarriage_return current line to top
513 * z<NL> like zcarriage_return
514 * z- current line to bottom
515 * also z+, z^ like ^F and ^B.
516 * A preceding count is line to use rather
517 * than current line. A count between z and
518 * specifier character changes the screen size
519 * for the redraw.
520 *
521 */
522 case 'z':
523 if (state == VISUAL) {
524 i = vgetcnt();
525 if (i > 0)
526 vsetsiz(i);
527 c = getesc();
528 if (c == 0)
529 continue;
530 }
531 vsave();
532 vzop(hadcnt, cnt, c);
533 continue;
534
535 /*
536 * Y Yank lines, abbreviation for y_ or yy.
537 * Yanked lines can be put later if no
538 * changes intervene, or can be put in named
539 * buffers and put anytime in this session.
540 */
541 case 'Y':
542 ungetkey('_');
543 c = 'y';
544 break;
545
546 /*
547 * J Join lines, 2 by default. Count is number
548 * of lines to join (no join operator sorry.)
549 */
550 case 'J':
551 forbid (dot == dol);
552 if (cnt == 1)
553 cnt = 2;
554 if (cnt > (i = dol - dot + 1))
555 cnt = i;
556 vsave();
557 vmacchng(1);
558 setLAST();
559 cursor = strend(linebuf);
560 vremote(cnt, join, 0);
561 notenam = (unsigned char *)"join";
562 vmoving = 0;
563 killU();
564 vreplace(vcline, cnt, 1);
565 if (!*cursor && cursor > linebuf)
566 cursor--;
567 if (notecnt == 2)
568 notecnt = 0;
569 vrepaint(cursor);
570 continue;
571
572 /*
573 * S Substitute text for whole lines, abbrev for c_.
574 * Count is number of lines to change.
575 */
576 case 'S':
577 ungetkey('_');
578 c = 'c';
579 break;
580
581 /*
582 * O Create a new line above current and accept new
583 * input text, to an escape, there.
584 * A count specifies, for dumb terminals when
585 * slowopen is not set, the number of physical
586 * line space to open on the screen.
587 *
588 * o Like O, but opens lines below.
589 */
590 case 'O':
591 case 'o':
592 vmacchng(1);
593 voOpen(c, cnt);
594 continue;
595
596 /*
597 * C Change text to end of line, short for c$.
598 */
599 case 'C':
600 if (*cursor) {
601 ungetkey('$'), c = 'c';
602 break;
603 }
604 goto appnd;
605
606 /*
607 * ~ Switch case of letter under cursor
608 */
609 case '~':
610 {
611 unsigned char mbuf[2049];
612 unsigned char *ccursor = cursor;
613 #ifdef PRESUNEUC
614 int tmp, length;
615 wchar_t wchar;
616 #else
617 int tmp, len, n;
618 wchar_t wc;
619 #endif /* PRESUNEUC */
620 unsigned char tmp1;
621 setLAST();
622 for (tmp = 0; tmp + 3 < 2048; ) {
623 /*
624 * Use multiple 'r' commands to replace
625 * alpha with alternate case.
626 */
627
628 if(cnt-- <= 0)
629 break;
630 #ifdef PRESUNEUC
631 length = mbtowc(&wchar, (char *)ccursor, MULTI_BYTE_MAX);
632 #else
633 len = mbtowc(&wc, (char *)ccursor, MULTI_BYTE_MAX);
634 #endif /* PRESUNEUC */
635 #ifdef PRESUNEUC
636 if(length > 1) {
637 #else
638 n = iswalpha(wc);
639 if(len > 1 && !iswalpha(wc)) {
640 #endif /* PRESUNEUC */
641 mbuf[tmp+0] = ' ';
642 tmp++;
643 #ifdef PRESUNEUC
644 ccursor += length;
645 #else
646 ccursor += len;
647 #endif /* PRESUNEUC */
648 continue;
649 }
650 mbuf[tmp] = 'r';
651 #ifdef PRESUNEUC
652 mbuf[tmp+1] = *ccursor++;
653 #else
654 ccursor += ((len > 0) ? len : 1);
655 #endif /* PRESUNEUC */
656 /*
657 * If pointing to an alpha character,
658 * change the case.
659 */
660
661 tmp1 = mbuf[tmp+1];
662 #ifdef PRESUNEUC
663 if (isupper((unsigned char)tmp1))
664 mbuf[tmp+1] = tolower((unsigned char)tmp1);
665 else
666 mbuf[tmp+1] = toupper((unsigned char)tmp1);
667 #else
668 if (iswupper(wc))
669 len = wctomb((char *)(mbuf + tmp + 1),
670 (wc = towlower(wc)));
671 else
672 len = wctomb((char *)(mbuf + tmp + 1),
673 (wc = towupper(wc)));
674 tmp += len - 1;
675 #endif /* PRESUNEUC */
676 if(*ccursor)
677 /*
678 * If at end of line do not advance
679 * to the next character, else use a
680 * space to advance 1 column.
681 */
682 mbuf[tmp+2] = ' ';
683 else {
684 mbuf[tmp+2] = '\0';
685 tmp +=3;
686 break;
687 }
688 tmp += 3;
689 }
690
691 mbuf[tmp] = 0;
692 macpush(mbuf, 1);
693 }
694 continue;
695
696
697 /*
698 * A Append at end of line, short for $a.
699 */
700 case 'A':
701 operate('$', 1);
702 appnd:
703 c = 'a';
704 /* fall into ... */
705
706 /*
707 * a Appends text after cursor. Text can continue
708 * through arbitrary number of lines.
709 */
710 case 'a':
711 if (*cursor) {
712 wchar_t wchar;
713 int length = mbtowc(&wchar, (char *)cursor, MULTI_BYTE_MAX);
714 if (state == HARDOPEN) {
715 if(length < 0) {
716 putoctal = 1;
717 putchar(*cursor);
718 putoctal = 0;
719 } else
720 putchar(wchar);
721 }
722 if(length < 0)
723 cursor++;
724 else
725 cursor += length;
726 }
727 goto insrt;
728
729 /*
730 * I Insert at beginning of whitespace of line,
731 * short for ^i.
732 */
733 case 'I':
734 operate('^', 1);
735 c = 'i';
736 /* fall into ... */
737
738 /*
739 * R Replace characters, one for one, by input
740 * (logically), like repeated r commands.
741 *
742 * BUG: This is like the typeover mode of many other
743 * editors, and is only rarely useful. Its
744 * implementation is a hack in a low level
745 * routine and it doesn't work very well, e.g.
746 * you can't move around within a R, etc.
747 */
748 case 'R':
749 /* fall into... */
750
751 /*
752 * i Insert text to an escape in the buffer.
753 * Text is arbitrary. This command reminds of
754 * the i command in bare teco.
755 */
756 case 'i':
757 insrt:
758 /*
759 * Common code for all the insertion commands.
760 * Save for redo, position cursor, prepare for append
761 * at command and in visual undo. Note that nothing
762 * is doomed, unless R when all is, and save the
763 * current line in a the undo temporary buffer.
764 */
765 vmacchng(1);
766 setLAST();
767 vcursat(cursor);
768 prepapp();
769 vnoapp();
770 doomed = c == 'R' ? 10000 : 0;
771 if(FIXUNDO)
772 vundkind = VCHNG;
773 vmoving = 0;
774 CP(vutmp, linebuf);
775
776 /*
777 * If this is a repeated command, then suppress
778 * fake insert mode on dumb terminals which looks
779 * ridiculous and wastes lots of time even at 9600B.
780 */
781 if (vglobp)
782 hold = HOLDQIK;
783 vappend(c, cnt, 0);
784 continue;
785
786 /*
787 * An attention, normally a DEL, just beeps.
788 * If you are a vi command within ex, then
789 * two ATTN's will drop you back to command mode.
790 */
791 case ATTN:
792 (void) beep();
793 if (initev || peekkey() != ATTN)
794 continue;
795 /* fall into... */
796
797 /*
798 * ^\ A quit always gets command mode.
799 */
800 case QUIT:
801 /*
802 * Have to be careful if we were called
803 * g/xxx/vi
804 * since a return will just start up again.
805 * So we simulate an interrupt.
806 */
807 if (inglobal)
808 onintr(0);
809 /* fall into... */
810
811 #ifdef notdef
812 /*
813 * q Quit back to command mode, unless called as
814 * vi on command line in which case dont do it
815 */
816 case 'q': /* quit */
817 if (initev) {
818 vsave();
819 CATCH
820 error(gettext("Q gets ex command mode, :q leaves vi"));
821 ENDCATCH
822 splitw = 0;
823 getDOT();
824 vrepaint(cursor);
825 continue;
826 }
827 #endif
828 /* fall into... */
829
830 /*
831 * Q Is like q, but always gets to command mode
832 * even if command line invocation was as vi.
833 */
834 case 'Q':
835 vsave();
836 /*
837 * If we are in the middle of a macro, throw away
838 * the rest and fix up undo.
839 * This code copied from getbr().
840 */
841 if (vmacp) {
842 vmacp = 0;
843 if (inopen == -1) /* don't mess up undo for esc esc */
844 vundkind = VMANY;
845 inopen = 1; /* restore old setting now that macro done */
846 }
847 ixlatctl(1);
848 return;
849
850
851 /*
852 * ZZ Like :x
853 */
854 case 'Z':
855 forbid(getkey() != 'Z');
856 oglobp = globp;
857 globp = (unsigned char *)"x";
858 vclrech(0);
859 goto gogo;
860
861 /*
862 * P Put back text before cursor or before current
863 * line. If text was whole lines goes back
864 * as whole lines. If part of a single line
865 * or parts of whole lines splits up current
866 * line to form many new lines.
867 * May specify a named buffer, or the delete
868 * saving buffers 1-9.
869 *
870 * p Like P but after rather than before.
871 */
872 case 'P':
873 case 'p':
874 vmoving = 0;
875 #ifdef XPG4
876 P_cursor_offset = 0;
877 #endif
878 #ifdef notdef
879 forbid (!vreg && value(vi_UNDOMACRO) && inopen < 0);
880 #endif
881 /*
882 * If previous delete was partial line, use an
883 * append or insert to put it back so as to
884 * use insert mode on intelligent terminals.
885 */
886 if (!vreg && DEL[0]) {
887 setLAST();
888 forbid ((unsigned char)DEL[128] == 0200);
889 vglobp = DEL;
890 ungetkey(c == 'p' ? 'a' : 'i');
891 goto reread;
892 }
893
894 /*
895 * If a register wasn't specified, then make
896 * sure there is something to put back.
897 */
898 forbid (!vreg && unddol == dol);
899 /*
900 * If we just did a macro the whole buffer is in
901 * the undo save area. We don't want to put THAT.
902 */
903 forbid (vundkind == VMANY && undkind==UNDALL);
904 vsave();
905 vmacchng(1);
906 setLAST();
907 i = 0;
908 if (vreg && partreg(vreg) || !vreg && pkill[0]) {
909 /*
910 * Restoring multiple lines which were partial
911 * lines; will leave cursor in middle
912 * of line after shoving restored text in to
913 * split the current line.
914 */
915 i++;
916 if (c == 'p' && *cursor)
917 cursor = nextchr(cursor);
918 } else {
919 /*
920 * In whole line case, have to back up dot
921 * for P; also want to clear cursor so
922 * cursor will eventually be positioned
923 * at the beginning of the first put line.
924 */
925 cursor = 0;
926 if (c == 'P') {
927 dot--, vcline--;
928 c = 'p';
929 }
930 }
931 killU();
932
933 /*
934 * The call to putreg can potentially
935 * bomb since there may be nothing in a named buffer.
936 * We thus put a catch in here. If we didn't and
937 * there was an error we would end up in command mode.
938 */
939 addr = dol; /* old dol */
940 CATCH
941 vremote(1,
942 vreg ? (int (*)())putreg : put, vreg);
943 ONERR
944 if (vreg == -1) {
945 splitw = 0;
946 if (op == 'P')
947 dot++, vcline++;
948 goto pfixup;
949 }
950 ENDCATCH
951 splitw = 0;
952 nlput = dol - addr + 1;
953 if (!i) {
954 /*
955 * Increment undap1, undap2 to make up
956 * for their incorrect initialization in the
957 * routine vremote before calling put/putreg.
958 */
959 if (FIXUNDO)
960 undap1++, undap2++;
961 vcline++;
962 nlput--;
963
964 /*
965 * After a put want current line first line,
966 * and dot was made the last line put in code
967 * run so far. This is why we increment vcline
968 * above and decrease dot here.
969 */
970 dot -= nlput - 1;
971 }
972 #ifdef TRACE
973 if (trace)
974 fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
975 #endif
976 vreplace(vcline, i, nlput);
977 #ifdef XPG4
978 if (op == 'P' && i > 0) {
979 dot += nlput - 1;
980 vcline += nlput - 1;
981 cursor += P_cursor_offset;
982 }
983 #endif
984 if (state != VISUAL) {
985 /*
986 * Special case in open mode.
987 * Force action on the screen when a single
988 * line is put even if it is identical to
989 * the current line, e.g. on YP; otherwise
990 * you can't tell anything happened.
991 */
992 vjumpto(dot, cursor, '.');
993 continue;
994 }
995 pfixup:
996 vrepaint(cursor);
997 vfixcurs();
998 continue;
999
1000 /*
1001 * ^^ Return to previous file.
1002 * Like a :e #, and thus can be used after a
1003 * "No Write" diagnostic.
1004 */
1005 case CTRL('^'):
1006 forbid (hadcnt);
1007 vsave();
1008 ckaw();
1009 oglobp = globp;
1010 if (value(vi_AUTOWRITE) && !value(vi_READONLY))
1011 globp = (unsigned char *)"e! #";
1012 else
1013 globp = (unsigned char *)"e #";
1014 goto gogo;
1015
1016 #ifdef TAG_STACK
1017 /*
1018 * ^T Pop the tag stack if enabled or else reset it
1019 * if not.
1020 */
1021 case CTRL('t'):
1022 forbid (hadcnt);
1023 vsave();
1024 oglobp = globp;
1025 globp = (unsigned char *) "pop";
1026 goto gogo;
1027 #endif
1028 /*
1029 * ^] Takes word after cursor as tag, and then does
1030 * tag command. Read ``go right to''.
1031 * This is not a search, so the wrapscan setting
1032 * must be ignored. If set, then it is unset
1033 * here and restored later.
1034 */
1035 case CTRL(']'):
1036 grabtag();
1037 oglobp = globp;
1038 if (value(vi_WRAPSCAN) == 0) {
1039 tag_reset_wrap = 1;
1040 value(vi_WRAPSCAN) = 1;
1041 }
1042 globp = (unsigned char *)"tag";
1043 goto gogo;
1044
1045 /*
1046 * & Like :&
1047 */
1048 case '&':
1049 oglobp = globp;
1050 globp = (unsigned char *)"&";
1051 goto gogo;
1052
1053 /*
1054 * ^G Bring up a status line at the bottom of
1055 * the screen, like a :file command.
1056 *
1057 * BUG: Was ^S but doesn't work in cbreak mode
1058 */
1059 case CTRL('g'):
1060 oglobp = globp;
1061 globp = (unsigned char *)"file";
1062 gogo:
1063 addr = dot;
1064 vsave();
1065 goto doinit;
1066
1067 #ifdef SIGTSTP
1068 /*
1069 * ^Z: suspend editor session and temporarily return
1070 * to shell. Only works with Berkeley/IIASA process
1071 * control in kernel.
1072 */
1073 case CTRL('z'):
1074 forbid(dosusp == 0);
1075 vsave();
1076 oglobp = globp;
1077 globp = (unsigned char *)"stop";
1078 goto gogo;
1079 #endif
1080
1081 /*
1082 * : Read a command from the echo area and
1083 * execute it in command mode.
1084 */
1085 case ':':
1086 forbid (hadcnt);
1087 vsave();
1088 i = tchng;
1089 addr = dot;
1090 if (readecho(c)) {
1091 esave[0] = 0;
1092 goto fixup;
1093 }
1094 getDOT();
1095 /*
1096 * Use the visual undo buffer to store the global
1097 * string for command mode, since it is idle right now.
1098 */
1099 oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
1100 doinit:
1101 esave[0] = 0;
1102 fixech();
1103
1104 /*
1105 * Have to finagle around not to lose last
1106 * character after this command (when run from ex
1107 * command mode). This is clumsy.
1108 */
1109 d = peekc; ungetchar(0);
1110 if (shouldpo) {
1111 /*
1112 * So after a "Hit return..." ":", we do
1113 * another "Hit return..." the next time
1114 */
1115 pofix();
1116 shouldpo = 0;
1117 }
1118 CATCH
1119 /*
1120 * Save old values of options so we can
1121 * notice when they change; switch into
1122 * cooked mode so we are interruptible.
1123 */
1124 onumber = value(vi_NUMBER);
1125 olist = value(vi_LIST);
1126 OPline = Pline;
1127 OPutchar = Putchar;
1128 #ifndef CBREAK
1129 vcook();
1130 #endif
1131 commands(1, 1);
1132 if (dot == zero && dol > zero)
1133 dot = one;
1134 #ifndef CBREAK
1135 vraw();
1136 #endif
1137 ONERR
1138 #ifndef CBREAK
1139 vraw();
1140 #endif
1141 copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t));
1142 ENDCATCH
1143 fixol();
1144 Pline = OPline;
1145 Putchar = OPutchar;
1146 ungetchar(d);
1147 globp = oglobp;
1148
1149 /*
1150 * If we ended up with no lines in the buffer, make
1151 * a line.
1152 */
1153 if (dot == zero) {
1154 fixzero();
1155 }
1156 splitw = 0;
1157
1158 /*
1159 * Special case: did list/number options change?
1160 */
1161 if (onumber != value(vi_NUMBER))
1162 setnumb(value(vi_NUMBER));
1163 if (olist != value(vi_LIST))
1164 setlist(value(vi_LIST));
1165
1166 fixup:
1167 /*
1168 * If a change occurred, other than
1169 * a write which clears changes, then
1170 * we should allow an undo even if .
1171 * didn't move.
1172 *
1173 * BUG: You can make this wrong by
1174 * tricking around with multiple commands
1175 * on one line of : escape, and including
1176 * a write command there, but it's not
1177 * worth worrying about.
1178 */
1179 if (FIXUNDO && tchng && tchng != i)
1180 vundkind = VMANY, cursor = 0;
1181
1182 /*
1183 * If we are about to do another :, hold off
1184 * updating of screen.
1185 */
1186 if (vcnt < 0 && Peekkey == ':') {
1187 getDOT();
1188 shouldpo = 1;
1189 continue;
1190 }
1191 shouldpo = 0;
1192
1193 /*
1194 * In the case where the file being edited is
1195 * new; e.g. if the initial state hasn't been
1196 * saved yet, then do so now.
1197 */
1198 if (unddol == truedol) {
1199 vundkind = VNONE;
1200 Vlines = lineDOL();
1201 if (!inglobal)
1202 savevis();
1203 addr = zero;
1204 vcnt = 0;
1205 if (esave[0] == 0)
1206 copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t));
1207 }
1208
1209 /*
1210 * If the current line moved reset the cursor position.
1211 */
1212 if (dot != addr) {
1213 vmoving = 0;
1214 cursor = 0;
1215 }
1216
1217 /*
1218 * If current line is not on screen or if we are
1219 * in open mode and . moved, then redraw.
1220 */
1221 i = vcline + (dot - addr);
1222 if(windowchg)
1223 windowinit();
1224 if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
1225 if (state == CRTOPEN)
1226 vup1();
1227 if (vcnt > 0)
1228 vcnt = 0;
1229 vjumpto(dot, (unsigned char *) 0, '.');
1230 } else {
1231 /*
1232 * Current line IS on screen.
1233 * If we did a [Hit return...] then
1234 * restore vcnt and clear screen if in visual
1235 */
1236 vcline = i;
1237 if (vcnt < 0) {
1238 vcnt = -vcnt;
1239 if (state == VISUAL)
1240 vclear();
1241 else if (state == CRTOPEN) {
1242 vcnt = 0;
1243 }
1244 }
1245
1246 /*
1247 * Limit max value of vcnt based on $
1248 */
1249 i = vcline + lineDOL() - lineDOT() + 1;
1250 if (i < vcnt)
1251 vcnt = i;
1252
1253 /*
1254 * Dirty and repaint.
1255 */
1256 vdirty(0, lines);
1257 vrepaint(cursor);
1258 }
1259
1260 /*
1261 * If in visual, put back the echo area
1262 * if it was clobbered.
1263 */
1264 if (state == VISUAL) {
1265 int sdc = destcol, sdl = destline;
1266
1267 splitw++;
1268 vigoto(WECHO, 0);
1269 for (i = 0; i < TUBECOLS - 1; i++) {
1270 if (esave[i] == 0)
1271 break;
1272 if(esave[i] != FILLER)
1273 (void) vputchar(esave[i]);
1274 }
1275 splitw = 0;
1276 vgoto(sdl, sdc);
1277 }
1278 if (tag_reset_wrap == 1) {
1279 tag_reset_wrap = 0;
1280 value(vi_WRAPSCAN) = 0;
1281 }
1282 continue;
1283
1284 /*
1285 * u undo the last changing command.
1286 */
1287 case 'u':
1288 vundo(1);
1289 continue;
1290
1291 /*
1292 * U restore current line to initial state.
1293 */
1294 case 'U':
1295 vUndo();
1296 continue;
1297
1298 fonfon:
1299 (void) beep();
1300 vmacp = 0;
1301 inopen = 1; /* might have been -1 */
1302 continue;
1303 }
1304
1305 /*
1306 * Rest of commands are decoded by the operate
1307 * routine.
1308 */
1309 operate(c, cnt);
1310 }
1311 }
1312
1313 /*
1314 * Grab the word after the cursor so we can look for it as a tag.
1315 */
1316 void
1317 grabtag(void)
1318 {
1319 unsigned char *cp, *dp;
1320
1321 cp = vpastwh(cursor);
1322 if (*cp) {
1323 dp = lasttag;
1324 do {
1325 if (dp < &lasttag[sizeof lasttag - 2])
1326 *dp++ = *cp;
1327 cp++;
1328 /* only allow ascii alphabetics */
1329 } while ((isascii(*cp) && isalpha(*cp)) || isdigit(*cp) || *cp == '_');
1330 *dp++ = 0;
1331 }
1332 }
1333
1334 /*
1335 * Before appending lines, set up addr1 and
1336 * the command mode undo information.
1337 */
1338 void
1339 prepapp(void)
1340 {
1341
1342 addr1 = dot;
1343 deletenone();
1344 addr1++;
1345 appendnone();
1346 }
1347
1348 /*
1349 * Execute function f with the address bounds addr1
1350 * and addr2 surrounding cnt lines starting at dot.
1351 */
1352 void
1353 vremote(cnt, f, arg)
1354 int cnt, (*f)(), arg;
1355 {
1356 int oing = inglobal;
1357
1358 addr1 = dot;
1359 addr2 = dot + cnt - 1;
1360 inglobal = 0;
1361 if (FIXUNDO)
1362 undap1 = undap2 = dot;
1363 (*f)(arg);
1364 inglobal = oing;
1365 if (FIXUNDO)
1366 vundkind = VMANY;
1367 /*
1368 * XPG6 assertion 273: For the following commands, don't set vmcurs
1369 * to 0, so that undo positions the cursor column correctly when
1370 * we've moved off the initial line that was changed eg. when G has
1371 * moved us off the line, or when a multi-line change was done.
1372 */
1373 if (lastcmd[0] != 'C' && lastcmd[0] != 'c' && lastcmd[0] != 'o' &&
1374 lastcmd[0] != 'R' && lastcmd[0] != 'S' && lastcmd[0] != 's' &&
1375 lastcmd[0] != 'i' && lastcmd[0] != 'a' && lastcmd[0] != 'A') {
1376 vmcurs = 0;
1377 }
1378 }
1379
1380 /*
1381 * Save the current contents of linebuf, if it has changed.
1382 */
1383 void
1384 vsave(void)
1385 {
1386 unsigned char temp[LBSIZE];
1387
1388 strncpy(temp, linebuf, sizeof (temp));
1389 if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
1390 /*
1391 * If the undo state is saved in the temporary buffer
1392 * vutmp, then we sync this into the temp file so that
1393 * we will be able to undo even after we have moved off
1394 * the line. It would be possible to associate a line
1395 * with vutmp but we assume that vutmp is only associated
1396 * with line dot (e.g. in case ':') above, so beware.
1397 */
1398 prepapp();
1399 strcLIN(vutmp);
1400 putmark(dot);
1401 vremote(1, yank, 0);
1402 vundkind = VMCHNG;
1403 notecnt = 0;
1404 undkind = UNDCHANGE;
1405 }
1406 /*
1407 * Get the line out of the temp file and do nothing if it hasn't
1408 * changed. This may seem like a loss, but the line will
1409 * almost always be in a read buffer so this may well avoid disk i/o.
1410 */
1411 getDOT();
1412 if (strncmp(linebuf, temp, sizeof (temp)) == 0)
1413 return;
1414 strcLIN(temp);
1415 putmark(dot);
1416 }
1417
1418 #undef forbid
1419 #define forbid(a) if (a) { (void) beep(); return; }
1420
1421 /*
1422 * Do a z operation.
1423 * Code here is rather long, and very uninteresting.
1424 */
1425 void
1426 vzop(bool hadcnt, int cnt, int c)
1427 {
1428 line *addr;
1429
1430 if (state != VISUAL) {
1431 /*
1432 * Z from open; always like a z=.
1433 * This code is a mess and should be cleaned up.
1434 */
1435 vmoveitup(1, 1);
1436 vgoto(outline, 0);
1437 ostop(normf);
1438 setoutt();
1439 addr2 = dot;
1440 vclear();
1441 destline = WECHO;
1442 zop2(Xhadcnt ? Xcnt : value(vi_WINDOW) - 1, '=');
1443 if (state == CRTOPEN)
1444 putnl();
1445 putNFL();
1446 termreset();
1447 Outchar = vputchar;
1448 (void)ostart();
1449 vcnt = 0;
1450 outline = destline = 0;
1451 vjumpto(dot, cursor, 0);
1452 return;
1453 }
1454 if (hadcnt) {
1455 addr = zero + cnt;
1456 if (addr < one)
1457 addr = one;
1458 if (addr > dol)
1459 addr = dol;
1460 markit(addr);
1461 } else
1462 switch (c) {
1463
1464 case '+':
1465 addr = dot + vcnt - vcline;
1466 break;
1467
1468 case '^':
1469 addr = dot - vcline - 1;
1470 forbid (addr < one);
1471 c = '-';
1472 break;
1473
1474 default:
1475 addr = dot;
1476 break;
1477 }
1478 switch (c) {
1479
1480 case '.':
1481 case '-':
1482 break;
1483
1484 case '^':
1485 forbid (addr <= one);
1486 break;
1487
1488 case '+':
1489 forbid (addr >= dol);
1490 /* fall into ... */
1491
1492 case CR:
1493 case NL:
1494 c = CR;
1495 break;
1496
1497 default:
1498 (void) beep();
1499 return;
1500 }
1501 vmoving = 0;
1502 vjumpto(addr, (unsigned char *)NOSTR, c);
1503 }
1504