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