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