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