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