Commit | Line | Data |
---|---|---|
8c102237 BJ |
1 | /* Copyright (c) 1979 Regents of the University of California */ |
2 | #include "ex.h" | |
3 | #include "ex_tty.h" | |
4 | #include "ex_vis.h" | |
5 | ||
6 | /* | |
7 | * This is the main routine for visual. | |
8 | * We here decode the count and possible named buffer specification | |
9 | * preceding a command and interpret a few of the commands. | |
10 | * Commands which involve a target (i.e. an operator) are decoded | |
11 | * in the routine operate in ex_voperate.c. | |
12 | */ | |
13 | ||
14 | #define forbid(a) { if (a) goto fonfon; } | |
15 | ||
16 | vmain() | |
17 | { | |
18 | register int c, cnt, i; | |
19 | char esave[TUBECOLS]; | |
20 | char *oglobp; | |
21 | char d; | |
22 | line *addr; | |
23 | int ind; | |
24 | int onumber, olist, (*OPline)(), (*OPutchar)(); | |
25 | ||
26 | /* | |
27 | * If we started as a vi command (on the command line) | |
28 | * then go process initial commands (recover, next or tag). | |
29 | */ | |
30 | if (initev) { | |
31 | oglobp = globp; | |
32 | globp = initev; | |
33 | hadcnt = cnt = 0; | |
34 | i = tchng; | |
35 | addr = dot; | |
36 | goto doinit; | |
37 | } | |
38 | ||
39 | /* | |
40 | * NB: | |
41 | * | |
42 | * The current line is always in the line buffer linebuf, | |
43 | * and the cursor at the position cursor. You should do | |
44 | * a vsave() before moving off the line to make sure the disk | |
45 | * copy is updated if it has changed, and a getDOT() to get | |
46 | * the line back if you mung linebuf. The motion | |
47 | * routines in ex_vwind.c handle most of this. | |
48 | */ | |
49 | for (;;) { | |
50 | /* | |
51 | * Decode a visual command. | |
52 | * First sync the temp file if there has been a reasonable | |
53 | * amount of change. Clear state for decoding of next | |
54 | * command. | |
55 | */ | |
56 | TSYNC(); | |
57 | vglobp = 0; | |
58 | vreg = 0; | |
59 | hold = 0; | |
60 | wcursor = 0; | |
61 | Xhadcnt = hadcnt = 0; | |
62 | Xcnt = cnt = 1; | |
63 | splitw = 0; | |
64 | if (i = holdupd) { | |
65 | if (state == VISUAL) | |
66 | ignore(peekkey()); | |
67 | holdupd = 0; | |
68 | /* | |
69 | if (LINE(0) < ZERO) { | |
70 | vclear(); | |
71 | vcnt = 0; | |
72 | i = 3; | |
73 | } | |
74 | */ | |
75 | if (state != VISUAL) { | |
76 | vcnt = 0; | |
77 | vsave(); | |
78 | vrepaint(cursor); | |
79 | } else if (i == 3) | |
80 | vredraw(WTOP); | |
81 | else | |
82 | vsync(WTOP); | |
83 | vfixcurs(); | |
84 | } | |
85 | ||
86 | /* | |
87 | * Gobble up counts and named buffer specifications. | |
88 | */ | |
89 | for (;;) { | |
90 | if (isdigit(peekkey()) && peekkey() != '0') { | |
91 | hadcnt = 1; | |
92 | cnt = vgetcnt(); | |
93 | forbid (cnt <= 0); | |
94 | } | |
95 | if (peekkey() != '"') | |
96 | break; | |
97 | ignore(getkey()), c = getkey(); | |
98 | /* | |
99 | * Buffer names be letters or digits. | |
100 | * But not '0' as that is the source of | |
101 | * an 'empty' named buffer spec in the routine | |
102 | * kshift (see ex_temp.c). | |
103 | */ | |
104 | forbid (c == '0' || !isalpha(c) && !isdigit(c)); | |
105 | vreg = c; | |
106 | } | |
107 | reread: | |
108 | /* | |
109 | * Come to reread from below after some macro expansions. | |
110 | * The call to map allows use of function key pads | |
111 | * by performing a terminal dependent mapping of inputs, | |
112 | * but notably not of two character inputs starting with | |
113 | * an escape (as from a VT52 keypad). This is useful for | |
114 | * e.g. DM1520's however. | |
115 | */ | |
116 | op = c = map(getkey()); | |
117 | ||
118 | /* | |
119 | * Begin to build an image of this command for possible | |
120 | * later repeat in the buffer workcmd. It will be copied | |
121 | * to lastcmd by the routine setLAST | |
122 | * if/when completely specified. | |
123 | */ | |
124 | lastcp = workcmd; | |
125 | if (!vglobp) | |
126 | *lastcp++ = c; | |
127 | ||
128 | /* | |
129 | * First level command decode. | |
130 | */ | |
131 | switch (c) { | |
132 | ||
133 | /* | |
134 | * ^L Clear screen e.g. after transmission error. | |
135 | */ | |
136 | case CTRL(l): | |
137 | vclear(); | |
138 | vdirty(0, vcnt); | |
139 | /* fall into... */ | |
140 | ||
141 | /* | |
142 | * ^R Retype screen, getting rid of @ lines. | |
143 | * If in open, equivalent to ^L. | |
144 | */ | |
145 | case CTRL(r): | |
146 | if (state != VISUAL) { | |
147 | /* | |
148 | * Get a clean line, throw away the | |
149 | * memory of what is displayed now, | |
150 | * and move back onto the current line. | |
151 | */ | |
152 | vclean(); | |
153 | vcnt = 0; | |
154 | vmoveto(dot, cursor, 0); | |
155 | continue; | |
156 | } | |
157 | vredraw(WTOP); | |
158 | /* | |
159 | * Weird glitch -- when we enter visual | |
160 | * in a very small window we may end up with | |
161 | * no lines on the screen because the line | |
162 | * at the top is too long. This forces the screen | |
163 | * to be expanded to make room for it (after | |
164 | * we have printed @'s ick showing we goofed). | |
165 | */ | |
166 | if (vcnt == 0) | |
167 | vrepaint(cursor); | |
168 | vfixcurs(); | |
169 | continue; | |
170 | ||
171 | /* | |
172 | * $ Escape just cancels the current command | |
173 | * with a little feedback. It would be | |
174 | * nice to handle function pads sending ESC-X; | |
175 | * here, using something else to replace ESC. | |
176 | */ | |
177 | case ESCAPE: | |
178 | beep(); | |
179 | continue; | |
180 | ||
181 | /* | |
182 | * . Repeat the last (modifying) open/visual command. | |
183 | */ | |
184 | case '.': | |
185 | /* | |
186 | * Check that there was a last command, and | |
187 | * take its count and named buffer unless they | |
188 | * were given anew. Special case if last command | |
189 | * referenced a numeric named buffer -- increment | |
190 | * the number and go to a named buffer again. | |
191 | * This allows a sequence like "1pu.u.u... | |
192 | * to successively look for stuff in the kill chain | |
193 | * much as one does in EMACS with C-Y and M-Y. | |
194 | */ | |
195 | forbid (lastcmd[0] == 0); | |
196 | if (hadcnt) | |
197 | lastcnt = cnt; | |
198 | if (vreg) | |
199 | lastreg = vreg; | |
200 | else if (isdigit(lastreg) && lastreg < '9') | |
201 | lastreg++; | |
202 | vreg = lastreg; | |
203 | cnt = lastcnt; | |
204 | hadcnt = lasthad; | |
205 | vglobp = lastcmd; | |
206 | goto reread; | |
207 | ||
208 | /* | |
209 | * ^U Scroll up. A count sticks around for | |
210 | * future scrolls as the scroll amount. | |
211 | * Attempt to hold the indentation from the | |
212 | * top of the screen (in logical lines). | |
213 | * | |
214 | * BUG: A ^U near the bottom of the screen | |
215 | * on a dumb terminal (which can't roll back) | |
216 | * causes the screen to be cleared and then | |
217 | * redrawn almost as it was. In this case | |
218 | * one should simply move the cursor. | |
219 | */ | |
220 | case CTRL(u): | |
221 | if (hadcnt) | |
222 | vSCROLL = cnt; | |
223 | cnt = vSCROLL; | |
224 | if (state == VISUAL) | |
225 | ind = vcline, cnt += ind; | |
226 | else | |
227 | ind = 0; | |
228 | vmoving = 0; | |
229 | vup(cnt, ind, 1); | |
230 | vnline(NOSTR); | |
231 | continue; | |
232 | ||
233 | /* | |
234 | * ^D Scroll down. Like scroll up. | |
235 | */ | |
236 | case CTRL(d): | |
237 | if (hadcnt) | |
238 | vSCROLL = cnt; | |
239 | cnt = vSCROLL; | |
240 | if (state == VISUAL) | |
241 | ind = vcnt - vcline - 1, cnt += ind; | |
242 | else | |
243 | ind = 0; | |
244 | vmoving = 0; | |
245 | vdown(cnt, ind, 1); | |
246 | vnline(NOSTR); | |
247 | continue; | |
248 | ||
249 | /* | |
250 | * m Mark position in mark register given | |
251 | * by following letter. Return is | |
252 | * accomplished via ' or `; former | |
253 | * to beginning of line where mark | |
254 | * was set, latter to column where marked. | |
255 | */ | |
256 | case 'm': | |
257 | /* | |
258 | * Getesc is generally used when a character | |
259 | * is read as a latter part of a command | |
260 | * to allow one to hit rubout/escape to cancel | |
261 | * what you have typed so far. These characters | |
262 | * are mapped to 0 by the subroutine. | |
263 | */ | |
264 | c = getesc(); | |
265 | if (c == 0) | |
266 | continue; | |
267 | ||
268 | /* | |
269 | * Markreg checks that argument is a letter | |
270 | * and also maps ' and ` to the end of the range | |
271 | * to allow '' or `` to reference the previous | |
272 | * context mark. | |
273 | */ | |
274 | c = markreg(c); | |
275 | forbid (c == 0); | |
276 | vsave(); | |
277 | names[c - 'a'] = (*dot &~ 01); | |
278 | ncols[c - 'a'] = cursor; | |
279 | anymarks = 1; | |
280 | continue; | |
281 | ||
282 | /* | |
283 | * ^F Window forwards, with 2 lines of continuity. | |
284 | * Count gives new screen size. | |
285 | */ | |
286 | case CTRL(f): | |
287 | vsave(); | |
288 | if (hadcnt) | |
289 | vsetsiz(cnt); | |
290 | if (vcnt > 2) { | |
291 | dot += (vcnt - vcline) - 2; | |
292 | vcnt = vcline = 0; | |
293 | } | |
294 | vzop(0, 0, '+'); | |
295 | continue; | |
296 | ||
297 | /* | |
298 | * ^B Window backwards, with 2 lines of continuity. | |
299 | * Inverse of ^F. | |
300 | */ | |
301 | case CTRL(b): | |
302 | vsave(); | |
303 | if (hadcnt) | |
304 | vsetsiz(cnt); | |
305 | if (one + vcline != dot && vcnt > 2) { | |
306 | dot -= vcline - 2; | |
307 | vcnt = vcline = 0; | |
308 | } | |
309 | vzop(0, 0, '^'); | |
310 | continue; | |
311 | ||
312 | /* | |
313 | * z Screen adjustment, taking a following character: | |
314 | * z<CR> current line to top | |
315 | * z<NL> like z<CR> | |
316 | * z- current line to bottom | |
317 | * also z+, z^ like ^F and ^B. | |
318 | * A preceding count is line to use rather | |
319 | * than current line. A count between z and | |
320 | * specifier character changes the screen size | |
321 | * for the redraw. | |
322 | * | |
323 | */ | |
324 | case 'z': | |
325 | if (state == VISUAL) { | |
326 | i = vgetcnt(); | |
327 | if (i > 0) | |
328 | vsetsiz(i); | |
329 | c = getesc(); | |
330 | if (c == 0) | |
331 | continue; | |
332 | } | |
333 | vsave(); | |
334 | vzop(hadcnt, cnt, c); | |
335 | continue; | |
336 | ||
337 | /* | |
338 | * Y Yank lines, abbreviation for y_ or yy. | |
339 | * Yanked lines can be put later if no | |
340 | * changes intervene, or can be put in named | |
341 | * buffers and put anytime in this session. | |
342 | */ | |
343 | case 'Y': | |
344 | ungetkey('_'); | |
345 | c = 'y'; | |
346 | break; | |
347 | ||
348 | /* | |
349 | * J Join lines, 2 by default. Count is number | |
350 | * of lines to join (no join operator sorry.) | |
351 | */ | |
352 | case 'J': | |
353 | forbid (dot == dol); | |
354 | if (cnt == 1) | |
355 | cnt = 2; | |
356 | if (cnt > (i = dol - dot + 1)) | |
357 | cnt = i; | |
358 | vsave(); | |
359 | setLAST(); | |
360 | cursor = strend(linebuf); | |
361 | vremote(cnt, join, 0); | |
362 | notenam = "join"; | |
363 | vmoving = 0; | |
364 | killU(); | |
365 | vreplace(vcline, cnt, 1); | |
366 | if (!*cursor && cursor > linebuf) | |
367 | cursor--; | |
368 | if (notecnt == 2) | |
369 | notecnt = 0; | |
370 | vrepaint(cursor); | |
371 | continue; | |
372 | ||
373 | /* | |
374 | * S Substitute text for whole lines, abbrev for c_. | |
375 | * Count is number of lines to change. | |
376 | */ | |
377 | case 'S': | |
378 | ungetkey('_'); | |
379 | c = 'c'; | |
380 | break; | |
381 | ||
382 | /* | |
383 | * O Create a new line above current and accept new | |
384 | * input text, to an escape, there. | |
385 | * A count specifies, for dumb terminals when | |
386 | * slowopen is not set, the number of physical | |
387 | * line space to open on the screen. | |
388 | * | |
389 | * o Like O, but opens lines below. | |
390 | */ | |
391 | case 'O': | |
392 | case 'o': | |
393 | voOpen(c, cnt); | |
394 | continue; | |
395 | ||
396 | /* | |
397 | * C Change text to end of line, short for c$. | |
398 | */ | |
399 | case 'C': | |
400 | if (*cursor) { | |
401 | ungetkey('$'), c = 'c'; | |
402 | break; | |
403 | } | |
404 | goto appnd; | |
405 | ||
406 | /* | |
407 | * A Append at end of line, short for $a. | |
408 | */ | |
409 | case 'A': | |
410 | operate('$', 1); | |
411 | appnd: | |
412 | c = 'a'; | |
413 | /* fall into ... */ | |
414 | ||
415 | /* | |
416 | * a Appends text after cursor. Text can continue | |
417 | * through arbitrary number of lines. | |
418 | */ | |
419 | case 'a': | |
420 | if (*cursor) { | |
421 | if (state == HARDOPEN) | |
422 | putchar(*cursor); | |
423 | cursor++; | |
424 | } | |
425 | goto insrt; | |
426 | ||
427 | /* | |
428 | * I Insert at beginning of whitespace of line, | |
429 | * short for ^i. | |
430 | */ | |
431 | case 'I': | |
432 | operate('^', 1); | |
433 | c = 'i'; | |
434 | /* fall into ... */ | |
435 | ||
436 | /* | |
437 | * R Replace characters, one for one, by input | |
438 | * (logically), like repeated r commands. | |
439 | * | |
440 | * BUG: This is like the typeover mode of many other | |
441 | * editors, and is only rarely useful. Its | |
442 | * implementation is a hack in a low level | |
443 | * routine and it doesn't work very well, e.g. | |
444 | * you can't move around within a R, etc. | |
445 | */ | |
446 | case 'R': | |
447 | /* fall into... */ | |
448 | ||
449 | /* | |
450 | * i Insert text to an escape in the buffer. | |
451 | * Text is arbitrary. This command reminds of | |
452 | * the i command in bare teco. | |
453 | */ | |
454 | case 'i': | |
455 | insrt: | |
456 | /* | |
457 | * Common code for all the insertion commands. | |
458 | * Save for redo, position cursor, prepare for append | |
459 | * at command and in visual undo. Note that nothing | |
460 | * is doomed, unless R when all is, and save the | |
461 | * current line in a the undo temporary buffer. | |
462 | */ | |
463 | setLAST(); | |
464 | vcursat(cursor); | |
465 | prepapp(); | |
466 | vnoapp(); | |
467 | doomed = c == 'R' ? 10000 : 0; | |
468 | vundkind = VCHNG; | |
469 | CP(vutmp, linebuf); | |
470 | ||
471 | /* | |
472 | * If this is a repeated command, then suppress | |
473 | * fake insert mode on dumb terminals which looks | |
474 | * ridiculous and wastes lots of time even at 9600B. | |
475 | */ | |
476 | if (vglobp) | |
477 | hold = HOLDQIK; | |
478 | vappend(c, cnt, 0); | |
479 | continue; | |
480 | ||
481 | /* | |
482 | * ^? An attention, normally a ^?, just beeps. | |
483 | * If you are a vi command within ex, then | |
484 | * two ATTN's will drop you back to command mode. | |
485 | */ | |
486 | case ATTN: | |
487 | beep(); | |
488 | if (initev || peekkey() != ATTN) | |
489 | continue; | |
490 | /* fall into... */ | |
491 | ||
492 | /* | |
493 | * ^\ A quit always gets command mode. | |
494 | */ | |
495 | case QUIT: | |
496 | /* | |
497 | * Have to be careful if we were called | |
498 | * g/xxx/vi | |
499 | * since a return will just start up again. | |
500 | * So we simulate an interrupt. | |
501 | */ | |
502 | if (inglobal) | |
503 | onintr(); | |
504 | /* fall into... */ | |
505 | ||
506 | /* | |
507 | * q Quit back to command mode, unless called as | |
508 | * vi on command line in which case dont do it | |
509 | */ | |
510 | case 'q': /* quit */ | |
511 | if (initev) { | |
512 | vsave(); | |
513 | CATCH | |
514 | error("Q gets ex command mode, :q leaves vi"); | |
515 | ENDCATCH | |
516 | splitw = 0; | |
517 | getDOT(); | |
518 | vrepaint(cursor); | |
519 | continue; | |
520 | } | |
521 | /* fall into... */ | |
522 | ||
523 | /* | |
524 | * Q Is like q, but always gets to command mode | |
525 | * even if command line invocation was as vi. | |
526 | */ | |
527 | case 'Q': | |
528 | vsave(); | |
529 | return; | |
530 | ||
531 | /* | |
532 | * P Put back text before cursor or before current | |
533 | * line. If text was whole lines goes back | |
534 | * as whole lines. If part of a single line | |
535 | * or parts of whole lines splits up current | |
536 | * line to form many new lines. | |
537 | * May specify a named buffer, or the delete | |
538 | * saving buffers 1-9. | |
539 | * | |
540 | * p Like P but after rather than before. | |
541 | */ | |
542 | case 'P': | |
543 | case 'p': | |
544 | vmoving = 0; | |
545 | /* | |
546 | * If previous delete was partial line, use an | |
547 | * append or insert to put it back so as to | |
548 | * use insert mode on intelligent terminals. | |
549 | */ | |
550 | if (!vreg && DEL[0]) { | |
551 | forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF); | |
552 | vglobp = DEL; | |
553 | ungetkey(c == 'p' ? 'a' : 'i'); | |
554 | goto reread; | |
555 | } | |
556 | ||
557 | /* | |
558 | * If a register wasn't specified, then make | |
559 | * sure there is something to put back. | |
560 | */ | |
561 | forbid (!vreg && unddol == dol); | |
562 | vsave(); | |
563 | setLAST(); | |
564 | i = 0; | |
565 | if (vreg && partreg(vreg) || !vreg && pkill[0]) { | |
566 | /* | |
567 | * Restoring multiple lines which were partial | |
568 | * lines; will leave cursor in middle | |
569 | * of line after shoving restored text in to | |
570 | * split the current line. | |
571 | */ | |
572 | i++; | |
573 | if (c == 'p' && *cursor) | |
574 | cursor++; | |
575 | } else { | |
576 | /* | |
577 | * In whole line case, have to back up dot | |
578 | * for P; also want to clear cursor so | |
579 | * cursor will eventually be positioned | |
580 | * at the beginning of the first put line. | |
581 | */ | |
582 | cursor = 0; | |
583 | if (c == 'P') { | |
584 | dot--, vcline--; | |
585 | c = 'p'; | |
586 | } | |
587 | } | |
588 | killU(); | |
589 | ||
590 | /* | |
591 | * The call to putreg can potentially | |
592 | * bomb since there may be nothing in a named buffer. | |
593 | * We thus put a catch in here. If we didn't and | |
594 | * there was an error we would end up in command mode. | |
595 | */ | |
596 | CATCH | |
597 | vremote(1, vreg ? putreg : put, vreg); | |
598 | ONERR | |
599 | if (vreg == -1) { | |
600 | splitw = 0; | |
601 | if (op == 'P') | |
602 | dot++, vcline++; | |
603 | goto pfixup; | |
604 | } | |
605 | ENDCATCH | |
606 | splitw = 0; | |
607 | if (!i) { | |
608 | /* | |
609 | * Increment undap1, undap2 to make up | |
610 | * for their incorrect initialization in the | |
611 | * routine vremote before calling put/putreg. | |
612 | */ | |
613 | undap1++, undap2++; | |
614 | vcline++; | |
615 | } | |
616 | ||
617 | /* | |
618 | * After a put want current line first line, | |
619 | * and dot was made the last line put in code run | |
620 | * so far. This is why we increment vcline above, | |
621 | * and decrease (usually) dot here. | |
622 | */ | |
623 | dot = undap1; | |
624 | vreplace(vcline, i, undap2 - undap1); | |
625 | if (state != VISUAL) { | |
626 | /* | |
627 | * Special case in open mode. | |
628 | * Force action on the screen when a single | |
629 | * line is put even if it is identical to | |
630 | * the current line, e.g. on YP; otherwise | |
631 | * you can't tell anything happened. | |
632 | */ | |
633 | vjumpto(dot, cursor, '.'); | |
634 | continue; | |
635 | } | |
636 | pfixup: | |
637 | vrepaint(cursor); | |
638 | vfixcurs(); | |
639 | continue; | |
640 | ||
641 | /* | |
642 | * ^^ Return to previous context. Like a 't | |
643 | * if that mark is set since tag sets that | |
644 | * mark if it stays in same file. Else | |
645 | * like a :e #, and thus can be used after a | |
646 | * "No Write" diagnostic. | |
647 | */ | |
648 | case CTRL(^): | |
649 | if (hadcnt) | |
650 | vsetsiz(cnt); | |
651 | addr = getmark('t'); | |
652 | if (addr != 0) { | |
653 | markit(addr); | |
654 | vupdown(addr - dot, NOSTR); | |
655 | continue; | |
656 | } | |
657 | oglobp = globp; | |
658 | globp = "e #"; | |
659 | goto gogo; | |
660 | ||
661 | /* | |
662 | * ^] Takes word after cursor as tag, and then does | |
663 | * tag command. Read ``go right to''. | |
664 | */ | |
665 | case CTRL(]): | |
666 | grabtag(); | |
667 | oglobp = globp; | |
668 | globp = "tag"; | |
669 | goto gogo; | |
670 | ||
671 | /* | |
672 | * ^G Bring up a status line at the bottom of | |
673 | * the screen, like a :file command. | |
674 | * | |
675 | * BUG: Was ^S but doesn't work in cbreak mode | |
676 | */ | |
677 | case CTRL(g): | |
678 | oglobp = globp; | |
679 | globp = "file"; | |
680 | gogo: | |
681 | addr = dot; | |
682 | vsave(); | |
683 | goto doinit; | |
684 | ||
685 | /* | |
686 | * : Read a command from the echo area and | |
687 | * execute it in command mode. | |
688 | */ | |
689 | case ':': | |
690 | if (hadcnt) | |
691 | vsetsiz(cnt); | |
692 | vsave(); | |
693 | i = tchng; | |
694 | addr = dot; | |
695 | if (readecho(c)) { | |
696 | esave[0] = 0; | |
697 | goto fixup; | |
698 | } | |
699 | /* | |
700 | * Use the visual undo buffer to store the global | |
701 | * string for command mode, since it is idle right now. | |
702 | */ | |
703 | oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp; | |
704 | doinit: | |
705 | esave[0] = 0; | |
706 | fixech(); | |
707 | ||
708 | /* | |
709 | * Have to finagle around not to lose last | |
710 | * character after this command (when run from ex | |
711 | * command mode). This is clumsy. | |
712 | */ | |
713 | d = peekc; ungetchar(0); | |
714 | CATCH | |
715 | /* | |
716 | * Save old values of options so we can | |
717 | * notice when they change; switch into | |
718 | * cooked mode so we are interruptible. | |
719 | */ | |
720 | onumber = value(NUMBER); | |
721 | olist = value(LIST); | |
722 | OPline = Pline; | |
723 | OPutchar = Putchar; | |
724 | #ifdef V6 | |
725 | vcook(); | |
726 | #endif | |
727 | commands(1, 1); | |
728 | if (dot == zero && dol > zero) | |
729 | dot = one; | |
730 | #ifdef V6 | |
731 | vraw(); | |
732 | #endif | |
733 | ONERR | |
734 | #ifdef V6 | |
735 | vraw(); | |
736 | #endif | |
737 | copy(esave, vtube[WECHO], TUBECOLS); | |
738 | ENDCATCH | |
739 | fixol(); | |
740 | Pline = OPline; | |
741 | Putchar = OPutchar; | |
742 | ungetchar(d); | |
743 | globp = oglobp; | |
744 | ||
745 | /* | |
746 | * If we ended up with no lines in the buffer, make | |
747 | * a line, and don't consider the buffer changed. | |
748 | */ | |
749 | if (dot == zero) { | |
750 | fixzero(); | |
751 | sync(); | |
752 | } | |
753 | splitw = 0; | |
754 | ||
755 | /* | |
756 | * Special case: did list/number options change? | |
757 | */ | |
758 | if (onumber != value(NUMBER)) | |
759 | setnumb(value(NUMBER)); | |
760 | if (olist != value(LIST)) | |
761 | setlist(value(LIST)); | |
762 | ||
763 | fixup: | |
764 | /* | |
765 | * If a change occurred, other than | |
766 | * a write which clears changes, then | |
767 | * we should allow an undo even if . | |
768 | * didn't move. | |
769 | * | |
770 | * BUG: You can make this wrong by | |
771 | * tricking around with multiple commands | |
772 | * on one line of : escape, and including | |
773 | * a write command there, but its not | |
774 | * worth worrying about. | |
775 | */ | |
776 | if (tchng && tchng != i) | |
777 | vundkind = VMANY, cursor = 0; | |
778 | ||
779 | /* | |
780 | * If we are about to do another :, hold off | |
781 | * updating of screen. | |
782 | */ | |
783 | if (vcnt < 0 && Peekkey == ':') { | |
784 | getDOT(); | |
785 | continue; | |
786 | } | |
787 | ||
788 | /* | |
789 | * In the case where the file being edited is | |
790 | * new; e.g. if the initial state hasn't been | |
791 | * saved yet, then do so now. | |
792 | */ | |
793 | if (unddol == truedol) { | |
794 | vundkind = VNONE; | |
795 | Vlines = lineDOL(); | |
796 | if (!inglobal) | |
797 | savevis(); | |
798 | addr = zero; | |
799 | vcnt = 0; | |
800 | if (esave[0] == 0) | |
801 | copy(esave, vtube[WECHO], TUBECOLS); | |
802 | } | |
803 | ||
804 | /* | |
805 | * If the current line moved reset the cursor position. | |
806 | */ | |
807 | if (dot != addr) { | |
808 | vmoving = 0; | |
809 | cursor = 0; | |
810 | } | |
811 | ||
812 | /* | |
813 | * If current line is not on screen or if we are | |
814 | * in open mode and . moved, then redraw. | |
815 | */ | |
816 | i = vcline + (dot - addr); | |
817 | if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) { | |
818 | if (state == CRTOPEN) | |
819 | vup1(); | |
820 | if (vcnt > 0) | |
821 | vcnt = 0; | |
822 | vjumpto(dot, (char *) 0, '.'); | |
823 | } else { | |
824 | /* | |
825 | * Current line IS on screen. | |
826 | * If we did a [Hit return...] then | |
827 | * restore vcnt and clear screen if in visual | |
828 | */ | |
829 | vcline = i; | |
830 | if (vcnt < 0) { | |
831 | vcnt = -vcnt; | |
832 | if (state == VISUAL) | |
833 | vclear(); | |
834 | else if (state == CRTOPEN) | |
835 | vcnt = 0; | |
836 | } | |
837 | ||
838 | /* | |
839 | * Limit max value of vcnt based on $ | |
840 | */ | |
841 | i = vcline + lineDOL() - lineDOT() + 1; | |
842 | if (i < vcnt) | |
843 | vcnt = i; | |
844 | ||
845 | /* | |
846 | * Dirty and repaint. | |
847 | */ | |
848 | vdirty(0, LINES); | |
849 | vrepaint(cursor); | |
850 | } | |
851 | ||
852 | /* | |
853 | * If in visual, put back the echo area | |
854 | * if it was clobberred. | |
855 | */ | |
856 | if (state == VISUAL) { | |
857 | int sdc = destcol, sdl = destline; | |
858 | ||
859 | splitw++; | |
860 | vigoto(WECHO, 0); | |
861 | for (i = 0; i < TUBECOLS - 1; i++) { | |
862 | if (esave[i] == 0) | |
863 | break; | |
864 | vputchar(esave[i]); | |
865 | } | |
866 | splitw = 0; | |
867 | vgoto(sdl, sdc); | |
868 | } | |
869 | continue; | |
870 | ||
871 | /* | |
872 | * u undo the last changing command. | |
873 | */ | |
874 | case 'u': | |
875 | vundo(); | |
876 | continue; | |
877 | ||
878 | /* | |
879 | * U restore current line to initial state. | |
880 | */ | |
881 | case 'U': | |
882 | vUndo(); | |
883 | continue; | |
884 | ||
885 | fonfon: | |
886 | beep(); | |
887 | continue; | |
888 | } | |
889 | ||
890 | /* | |
891 | * Rest of commands are decoded by the operate | |
892 | * routine. | |
893 | */ | |
894 | operate(c, cnt); | |
895 | } | |
896 | } | |
897 | ||
898 | /* | |
899 | * Grab the word after the cursor so we can look for it as a tag. | |
900 | */ | |
901 | grabtag() | |
902 | { | |
903 | register char *cp, *dp; | |
904 | ||
905 | cp = vpastwh(cursor); | |
906 | if (*cp) { | |
907 | dp = lasttag; | |
908 | do { | |
909 | if (dp < &lasttag[sizeof lasttag - 2]) | |
910 | *dp++ = *cp; | |
911 | cp++; | |
912 | } while (isalpha(*cp) || isdigit(*cp) || *cp == '_'); | |
913 | *dp++ = 0; | |
914 | } | |
915 | } | |
916 | ||
917 | /* | |
918 | * Before appending lines, set up addr1 and | |
919 | * the command mode undo information. | |
920 | */ | |
921 | prepapp() | |
922 | { | |
923 | ||
924 | addr1 = dot + 1; | |
925 | deletenone(); | |
926 | appendnone(); | |
927 | } | |
928 | ||
929 | /* | |
930 | * Execute function f with the address bounds addr1 | |
931 | * and addr2 surrounding cnt lines starting at dot. | |
932 | */ | |
933 | vremote(cnt, f, arg) | |
934 | int cnt, (*f)(), arg; | |
935 | { | |
936 | register int oing = inglobal; | |
937 | ||
938 | addr1 = dot; | |
939 | addr2 = dot + cnt - 1; | |
940 | undap1 = undap2 = dot; | |
941 | inglobal = 0; | |
942 | (*f)(arg); | |
943 | inglobal = oing; | |
944 | vundkind = VMANY; | |
945 | vmcurs = 0; | |
946 | } | |
947 | ||
948 | /* | |
949 | * Save the current contents of linebuf, if it has changed. | |
950 | */ | |
951 | vsave() | |
952 | { | |
953 | char temp[LBSIZE]; | |
954 | ||
955 | CP(temp, linebuf); | |
956 | if (vundkind == VCHNG || vundkind == VCAPU) { | |
957 | /* | |
958 | * If the undo state is saved in the temporary buffer | |
959 | * vutmp, then we sync this into the temp file so that | |
960 | * we will be able to undo even after we have moved off | |
961 | * the line. It would be possible to associate a line | |
962 | * with vutmp but we assume that vutmp is only associated | |
963 | * with line dot (e.g. in case ':') above, so beware. | |
964 | */ | |
965 | prepapp(); | |
966 | strcLIN(vutmp); | |
967 | putmark(dot); | |
968 | vremote(1, yank, 0); | |
969 | vundkind = VMCHNG; | |
970 | notecnt = 0; | |
971 | undkind = UNDCHANGE; | |
972 | } | |
973 | /* | |
974 | * Get the line out of the temp file and do nothing if it hasn't | |
975 | * changed. This may seem like a loss, but the line will | |
976 | * almost always be in a read buffer so this may well avoid disk i/o. | |
977 | */ | |
978 | getDOT(); | |
979 | if (strcmp(linebuf, temp) == 0) | |
980 | return; | |
981 | strcLIN(temp); | |
982 | putmark(dot); | |
983 | } | |
984 | ||
985 | #undef forbid | |
986 | #define forbid(a) if (a) { beep(); return; } | |
987 | ||
988 | /* | |
989 | * Do a z operation. | |
990 | * Code here is rather long, and very uninteresting. | |
991 | */ | |
992 | vzop(hadcnt, cnt, c) | |
993 | bool hadcnt; | |
994 | int cnt; | |
995 | register int c; | |
996 | { | |
997 | register line *addr; | |
998 | ||
999 | if (state != VISUAL) { | |
1000 | /* | |
1001 | * Z from open; always like a z=. | |
1002 | * This code is a mess and should be cleaned up. | |
1003 | */ | |
1004 | vmoveitup(1); | |
1005 | vgoto(outline, 0); | |
1006 | ostop(normf); | |
1007 | setoutt(); | |
1008 | addr2 = dot; | |
1009 | vclear(); | |
1010 | destline = WECHO; | |
1011 | zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '='); | |
1012 | if (state == CRTOPEN) | |
1013 | putnl(); | |
1014 | putNFL(); | |
1015 | termreset(); | |
1016 | Outchar = vputchar; | |
1017 | ignore(ostart()); | |
1018 | vcnt = 0; | |
1019 | outline = destline = 0; | |
1020 | vjumpto(dot, cursor, 0); | |
1021 | return; | |
1022 | } | |
1023 | if (hadcnt) { | |
1024 | addr = zero + cnt; | |
1025 | if (addr < one) | |
1026 | addr = one; | |
1027 | if (addr > dol) | |
1028 | addr = dol; | |
1029 | markit(addr); | |
1030 | } else | |
1031 | switch (c) { | |
1032 | ||
1033 | case '+': | |
1034 | addr = dot + vcnt - vcline; | |
1035 | break; | |
1036 | ||
1037 | case '^': | |
1038 | addr = dot - vcline - 1; | |
1039 | forbid (addr < one); | |
1040 | c = '-'; | |
1041 | break; | |
1042 | ||
1043 | default: | |
1044 | addr = dot; | |
1045 | break; | |
1046 | } | |
1047 | switch (c) { | |
1048 | ||
1049 | case '.': | |
1050 | case '-': | |
1051 | break; | |
1052 | ||
1053 | case '^': | |
1054 | forbid (addr <= one); | |
1055 | break; | |
1056 | ||
1057 | case '+': | |
1058 | forbid (addr >= dol); | |
1059 | /* fall into ... */ | |
1060 | ||
1061 | case CR: | |
1062 | case NL: | |
1063 | c = CR; | |
1064 | break; | |
1065 | ||
1066 | default: | |
1067 | beep(); | |
1068 | return; | |
1069 | } | |
1070 | vmoving = 0; | |
1071 | vjumpto(addr, NOSTR, c); | |
1072 | } |