1 | /*- |
---|
2 | * Copyright (c) 1992, 1993, 1994 |
---|
3 | * The Regents of the University of California. All rights reserved. |
---|
4 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 |
---|
5 | * Keith Bostic. All rights reserved. |
---|
6 | * |
---|
7 | * See the LICENSE file for redistribution information. |
---|
8 | */ |
---|
9 | |
---|
10 | #include "config.h" |
---|
11 | |
---|
12 | #ifndef lint |
---|
13 | static const char sccsid[] = "@(#)vi.c 10.57 (Berkeley) 10/13/96"; |
---|
14 | #endif /* not lint */ |
---|
15 | |
---|
16 | #include <sys/types.h> |
---|
17 | #include <sys/queue.h> |
---|
18 | #include <sys/time.h> |
---|
19 | |
---|
20 | #include <bitstring.h> |
---|
21 | #include <ctype.h> |
---|
22 | #include <errno.h> |
---|
23 | #include <limits.h> |
---|
24 | #include <stdio.h> |
---|
25 | #include <stdlib.h> |
---|
26 | #include <string.h> |
---|
27 | #include <unistd.h> |
---|
28 | |
---|
29 | #include "../common/common.h" |
---|
30 | #include "vi.h" |
---|
31 | |
---|
32 | typedef enum { |
---|
33 | GC_ERR, GC_ERR_NOFLUSH, GC_EVENT, GC_FATAL, GC_INTERRUPT, GC_OK |
---|
34 | } gcret_t; |
---|
35 | |
---|
36 | static VIKEYS const |
---|
37 | *v_alias __P((SCR *, VICMD *, VIKEYS const *)); |
---|
38 | static gcret_t v_cmd __P((SCR *, VICMD *, VICMD *, VICMD *, int *, int *)); |
---|
39 | static int v_count __P((SCR *, ARG_CHAR_T, u_long *)); |
---|
40 | static void v_dtoh __P((SCR *)); |
---|
41 | static int v_init __P((SCR *)); |
---|
42 | static gcret_t v_key __P((SCR *, int, EVENT *, u_int32_t)); |
---|
43 | static int v_keyword __P((SCR *)); |
---|
44 | static int v_motion __P((SCR *, VICMD *, VICMD *, int *)); |
---|
45 | |
---|
46 | #if defined(DEBUG) && defined(COMLOG) |
---|
47 | static void v_comlog __P((SCR *, VICMD *)); |
---|
48 | #endif |
---|
49 | |
---|
50 | /* |
---|
51 | * Side-effect: |
---|
52 | * The dot structure can be set by the underlying vi functions, |
---|
53 | * see v_Put() and v_put(). |
---|
54 | */ |
---|
55 | #define DOT (&VIP(sp)->sdot) |
---|
56 | #define DOTMOTION (&VIP(sp)->sdotmotion) |
---|
57 | |
---|
58 | /* |
---|
59 | * vi -- |
---|
60 | * Main vi command loop. |
---|
61 | * |
---|
62 | * PUBLIC: int vi __P((SCR **)); |
---|
63 | */ |
---|
64 | int |
---|
65 | vi(spp) |
---|
66 | SCR **spp; |
---|
67 | { |
---|
68 | GS *gp; |
---|
69 | MARK abs; |
---|
70 | SCR *next, *sp; |
---|
71 | VICMD cmd, *vp; |
---|
72 | VI_PRIVATE *vip; |
---|
73 | int comcount, mapped, rval; |
---|
74 | |
---|
75 | /* Get the first screen. */ |
---|
76 | sp = *spp; |
---|
77 | gp = sp->gp; |
---|
78 | |
---|
79 | /* Initialize the command structure. */ |
---|
80 | vp = &cmd; |
---|
81 | memset(vp, 0, sizeof(VICMD)); |
---|
82 | |
---|
83 | /* Reset strange attraction. */ |
---|
84 | F_SET(vp, VM_RCM_SET); |
---|
85 | |
---|
86 | /* Initialize the vi screen. */ |
---|
87 | if (v_init(sp)) |
---|
88 | return (1); |
---|
89 | |
---|
90 | /* Set the focus. */ |
---|
91 | (void)sp->gp->scr_rename(sp, sp->frp->name, 1); |
---|
92 | |
---|
93 | for (vip = VIP(sp), rval = 0;;) { |
---|
94 | /* Resolve messages. */ |
---|
95 | if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0)) |
---|
96 | goto ret; |
---|
97 | |
---|
98 | /* |
---|
99 | * If not skipping a refresh, return to command mode and |
---|
100 | * refresh the screen. |
---|
101 | */ |
---|
102 | if (F_ISSET(vip, VIP_S_REFRESH)) |
---|
103 | F_CLR(vip, VIP_S_REFRESH); |
---|
104 | else { |
---|
105 | sp->showmode = SM_COMMAND; |
---|
106 | if (vs_refresh(sp, 0)) |
---|
107 | goto ret; |
---|
108 | } |
---|
109 | |
---|
110 | /* Set the new favorite position. */ |
---|
111 | if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) { |
---|
112 | F_CLR(vip, VIP_RCM_LAST); |
---|
113 | (void)vs_column(sp, &sp->rcm); |
---|
114 | } |
---|
115 | |
---|
116 | /* |
---|
117 | * If not currently in a map, log the cursor position, |
---|
118 | * and set a flag so that this command can become the |
---|
119 | * DOT command. |
---|
120 | */ |
---|
121 | if (MAPPED_KEYS_WAITING(sp)) |
---|
122 | mapped = 1; |
---|
123 | else { |
---|
124 | if (log_cursor(sp)) |
---|
125 | goto err; |
---|
126 | mapped = 0; |
---|
127 | } |
---|
128 | |
---|
129 | /* |
---|
130 | * There may be an ex command waiting, and we returned here |
---|
131 | * only because we exited a screen or file. In this case, |
---|
132 | * we simply go back into the ex parser. |
---|
133 | */ |
---|
134 | if (EXCMD_RUNNING(gp)) { |
---|
135 | vp->kp = &vikeys[':']; |
---|
136 | goto ex_continue; |
---|
137 | } |
---|
138 | |
---|
139 | /* Refresh the command structure. */ |
---|
140 | memset(vp, 0, sizeof(VICMD)); |
---|
141 | |
---|
142 | /* |
---|
143 | * We get a command, which may or may not have an associated |
---|
144 | * motion. If it does, we get it too, calling its underlying |
---|
145 | * function to get the resulting mark. We then call the |
---|
146 | * command setting the cursor to the resulting mark. |
---|
147 | * |
---|
148 | * !!! |
---|
149 | * Vi historically flushed mapped characters on error, but |
---|
150 | * entering extra <escape> characters at the beginning of |
---|
151 | * a map wasn't considered an error -- in fact, users would |
---|
152 | * put leading <escape> characters in maps to clean up vi |
---|
153 | * state before the map was interpreted. Beauty! |
---|
154 | */ |
---|
155 | switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) { |
---|
156 | case GC_ERR: |
---|
157 | goto err; |
---|
158 | case GC_ERR_NOFLUSH: |
---|
159 | goto gc_err_noflush; |
---|
160 | case GC_EVENT: |
---|
161 | if (v_event_exec(sp, vp)) |
---|
162 | goto err; |
---|
163 | goto gc_event; |
---|
164 | case GC_FATAL: |
---|
165 | goto ret; |
---|
166 | case GC_INTERRUPT: |
---|
167 | goto intr; |
---|
168 | case GC_OK: |
---|
169 | break; |
---|
170 | } |
---|
171 | |
---|
172 | /* Check for security setting. */ |
---|
173 | if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) { |
---|
174 | ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE); |
---|
175 | goto err; |
---|
176 | } |
---|
177 | |
---|
178 | /* |
---|
179 | * Historical practice: if a dot command gets a new count, |
---|
180 | * any motion component goes away, i.e. "d3w2." deletes a |
---|
181 | * total of 5 words. |
---|
182 | */ |
---|
183 | if (F_ISSET(vp, VC_ISDOT) && comcount) |
---|
184 | DOTMOTION->count = 1; |
---|
185 | |
---|
186 | /* Copy the key flags into the local structure. */ |
---|
187 | F_SET(vp, vp->kp->flags); |
---|
188 | |
---|
189 | /* Prepare to set the previous context. */ |
---|
190 | if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) { |
---|
191 | abs.lno = sp->lno; |
---|
192 | abs.cno = sp->cno; |
---|
193 | } |
---|
194 | |
---|
195 | /* |
---|
196 | * Set the three cursor locations to the current cursor. The |
---|
197 | * underlying routines don't bother if the cursor doesn't move. |
---|
198 | * This also handles line commands (e.g. Y) defaulting to the |
---|
199 | * current line. |
---|
200 | */ |
---|
201 | vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno; |
---|
202 | vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno; |
---|
203 | |
---|
204 | /* |
---|
205 | * Do any required motion; v_motion sets the from MARK and the |
---|
206 | * line mode flag, as well as the VM_RCM flags. |
---|
207 | */ |
---|
208 | if (F_ISSET(vp, V_MOTION) && |
---|
209 | v_motion(sp, DOTMOTION, vp, &mapped)) { |
---|
210 | if (INTERRUPTED(sp)) |
---|
211 | goto intr; |
---|
212 | goto err; |
---|
213 | } |
---|
214 | |
---|
215 | /* |
---|
216 | * If a count is set and the command is line oriented, set the |
---|
217 | * to MARK here relative to the cursor/from MARK. This is for |
---|
218 | * commands that take both counts and motions, i.e. "4yy" and |
---|
219 | * "y%". As there's no way the command can know which the user |
---|
220 | * did, we have to do it here. (There are commands that are |
---|
221 | * line oriented and that take counts ("#G", "#H"), for which |
---|
222 | * this calculation is either completely meaningless or wrong. |
---|
223 | * Each command must validate the value for itself. |
---|
224 | */ |
---|
225 | if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE)) |
---|
226 | vp->m_stop.lno += vp->count - 1; |
---|
227 | |
---|
228 | /* Increment the command count. */ |
---|
229 | ++sp->ccnt; |
---|
230 | |
---|
231 | #if defined(DEBUG) && defined(COMLOG) |
---|
232 | v_comlog(sp, vp); |
---|
233 | #endif |
---|
234 | /* Call the function. */ |
---|
235 | ex_continue: if (vp->kp->func(sp, vp)) |
---|
236 | goto err; |
---|
237 | gc_event: |
---|
238 | #ifdef DEBUG |
---|
239 | /* Make sure no function left the temporary space locked. */ |
---|
240 | if (F_ISSET(gp, G_TMP_INUSE)) { |
---|
241 | F_CLR(gp, G_TMP_INUSE); |
---|
242 | msgq(sp, M_ERR, |
---|
243 | "232|vi: temporary buffer not released"); |
---|
244 | } |
---|
245 | #endif |
---|
246 | /* |
---|
247 | * If we're exiting this screen, move to the next one, or, if |
---|
248 | * there aren't any more, return to the main editor loop. The |
---|
249 | * ordering is careful, don't discard the contents of sp until |
---|
250 | * the end. |
---|
251 | */ |
---|
252 | if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { |
---|
253 | if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE))) |
---|
254 | goto ret; |
---|
255 | if (vs_discard(sp, &next)) |
---|
256 | goto ret; |
---|
257 | if (next == NULL && vs_swap(sp, &next, NULL)) |
---|
258 | goto ret; |
---|
259 | *spp = next; |
---|
260 | if (screen_end(sp)) |
---|
261 | goto ret; |
---|
262 | if (next == NULL) |
---|
263 | break; |
---|
264 | |
---|
265 | /* Switch screens, change focus. */ |
---|
266 | sp = next; |
---|
267 | vip = VIP(sp); |
---|
268 | (void)sp->gp->scr_rename(sp, sp->frp->name, 1); |
---|
269 | |
---|
270 | /* Don't trust the cursor. */ |
---|
271 | F_SET(vip, VIP_CUR_INVALID); |
---|
272 | |
---|
273 | continue; |
---|
274 | } |
---|
275 | |
---|
276 | /* |
---|
277 | * Set the dot command structure. |
---|
278 | * |
---|
279 | * !!! |
---|
280 | * Historically, commands which used mapped keys did not |
---|
281 | * set the dot command, with the exception of the text |
---|
282 | * input commands. |
---|
283 | */ |
---|
284 | if (F_ISSET(vp, V_DOT) && !mapped) { |
---|
285 | *DOT = cmd; |
---|
286 | F_SET(DOT, VC_ISDOT); |
---|
287 | |
---|
288 | /* |
---|
289 | * If a count was supplied for both the command and |
---|
290 | * its motion, the count was used only for the motion. |
---|
291 | * Turn the count back on for the dot structure. |
---|
292 | */ |
---|
293 | if (F_ISSET(vp, VC_C1RESET)) |
---|
294 | F_SET(DOT, VC_C1SET); |
---|
295 | |
---|
296 | /* VM flags aren't retained. */ |
---|
297 | F_CLR(DOT, VM_COMMASK | VM_RCM_MASK); |
---|
298 | } |
---|
299 | |
---|
300 | /* |
---|
301 | * Some vi row movements are "attracted" to the last position |
---|
302 | * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET |
---|
303 | * commands' candle. If the movement is to the EOL the vi |
---|
304 | * command handles it. If it's to the beginning, we handle it |
---|
305 | * here. |
---|
306 | * |
---|
307 | * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB |
---|
308 | * flag, but do the work themselves. The reason is that they |
---|
309 | * have to modify the column in case they're being used as a |
---|
310 | * motion component. Other similar commands (e.g. +, -) don't |
---|
311 | * have to modify the column because they are always line mode |
---|
312 | * operations when used as motions, so the column number isn't |
---|
313 | * of any interest. |
---|
314 | * |
---|
315 | * Does this totally violate the screen and editor layering? |
---|
316 | * You betcha. As they say, if you think you understand it, |
---|
317 | * you don't. |
---|
318 | */ |
---|
319 | switch (F_ISSET(vp, VM_RCM_MASK)) { |
---|
320 | case 0: |
---|
321 | case VM_RCM_SET: |
---|
322 | break; |
---|
323 | case VM_RCM: |
---|
324 | vp->m_final.cno = vs_rcm(sp, |
---|
325 | vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST)); |
---|
326 | break; |
---|
327 | case VM_RCM_SETLAST: |
---|
328 | F_SET(vip, VIP_RCM_LAST); |
---|
329 | break; |
---|
330 | case VM_RCM_SETFNB: |
---|
331 | vp->m_final.cno = 0; |
---|
332 | /* FALLTHROUGH */ |
---|
333 | case VM_RCM_SETNNB: |
---|
334 | if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno)) |
---|
335 | goto err; |
---|
336 | break; |
---|
337 | default: |
---|
338 | abort(); |
---|
339 | } |
---|
340 | |
---|
341 | /* Update the cursor. */ |
---|
342 | sp->lno = vp->m_final.lno; |
---|
343 | sp->cno = vp->m_final.cno; |
---|
344 | |
---|
345 | /* |
---|
346 | * Set the absolute mark -- set even if a tags or similar |
---|
347 | * command, since the tag may be moving to the same file. |
---|
348 | */ |
---|
349 | if ((F_ISSET(vp, V_ABS) || |
---|
350 | F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno || |
---|
351 | F_ISSET(vp, V_ABS_C) && |
---|
352 | (sp->lno != abs.lno || sp->cno != abs.cno)) && |
---|
353 | mark_set(sp, ABSMARK1, &abs, 1)) |
---|
354 | goto err; |
---|
355 | |
---|
356 | if (0) { |
---|
357 | err: if (v_event_flush(sp, CH_MAPPED)) |
---|
358 | msgq(sp, M_BERR, |
---|
359 | "110|Vi command failed: mapped keys discarded"); |
---|
360 | } |
---|
361 | |
---|
362 | /* |
---|
363 | * Check and clear interrupts. There's an obvious race, but |
---|
364 | * it's not worth fixing. |
---|
365 | */ |
---|
366 | gc_err_noflush: if (INTERRUPTED(sp)) { |
---|
367 | intr: CLR_INTERRUPT(sp); |
---|
368 | if (v_event_flush(sp, CH_MAPPED)) |
---|
369 | msgq(sp, M_ERR, |
---|
370 | "231|Interrupted: mapped keys discarded"); |
---|
371 | else |
---|
372 | msgq(sp, M_ERR, "236|Interrupted"); |
---|
373 | } |
---|
374 | |
---|
375 | /* If the last command switched screens, update. */ |
---|
376 | if (F_ISSET(sp, SC_SSWITCH)) { |
---|
377 | F_CLR(sp, SC_SSWITCH); |
---|
378 | |
---|
379 | /* |
---|
380 | * If the current screen is still displayed, it will |
---|
381 | * need a new status line. |
---|
382 | */ |
---|
383 | F_SET(sp, SC_STATUS); |
---|
384 | |
---|
385 | /* Switch screens, change focus. */ |
---|
386 | sp = sp->nextdisp; |
---|
387 | vip = VIP(sp); |
---|
388 | (void)sp->gp->scr_rename(sp, sp->frp->name, 1); |
---|
389 | |
---|
390 | /* Don't trust the cursor. */ |
---|
391 | F_SET(vip, VIP_CUR_INVALID); |
---|
392 | |
---|
393 | /* Refresh so we can display messages. */ |
---|
394 | if (vs_refresh(sp, 1)) |
---|
395 | return (1); |
---|
396 | } |
---|
397 | |
---|
398 | /* If the last command switched files, change focus. */ |
---|
399 | if (F_ISSET(sp, SC_FSWITCH)) { |
---|
400 | F_CLR(sp, SC_FSWITCH); |
---|
401 | (void)sp->gp->scr_rename(sp, sp->frp->name, 1); |
---|
402 | } |
---|
403 | |
---|
404 | /* If leaving vi, return to the main editor loop. */ |
---|
405 | if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) { |
---|
406 | *spp = sp; |
---|
407 | v_dtoh(sp); |
---|
408 | break; |
---|
409 | } |
---|
410 | } |
---|
411 | if (0) |
---|
412 | ret: rval = 1; |
---|
413 | return (rval); |
---|
414 | } |
---|
415 | |
---|
416 | #define KEY(key, ec_flags) { \ |
---|
417 | if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK) \ |
---|
418 | return (gcret); \ |
---|
419 | if (ev.e_value == K_ESCAPE) \ |
---|
420 | goto esc; \ |
---|
421 | if (F_ISSET(&ev.e_ch, CH_MAPPED)) \ |
---|
422 | *mappedp = 1; \ |
---|
423 | key = ev.e_c; \ |
---|
424 | } |
---|
425 | |
---|
426 | /* |
---|
427 | * The O_TILDEOP option makes the ~ command take a motion instead |
---|
428 | * of a straight count. This is the replacement structure we use |
---|
429 | * instead of the one currently in the VIKEYS table. |
---|
430 | * |
---|
431 | * XXX |
---|
432 | * This should probably be deleted -- it's not all that useful, and |
---|
433 | * we get help messages wrong. |
---|
434 | */ |
---|
435 | VIKEYS const tmotion = { |
---|
436 | v_mulcase, V_CNT|V_DOT|V_MOTION|VM_RCM_SET, |
---|
437 | "[count]~[count]motion", |
---|
438 | " ~ change case to motion" |
---|
439 | }; |
---|
440 | |
---|
441 | /* |
---|
442 | * v_cmd -- |
---|
443 | * |
---|
444 | * The command structure for vi is less complex than ex (and don't think |
---|
445 | * I'm not grateful!) The command syntax is: |
---|
446 | * |
---|
447 | * [count] [buffer] [count] key [[motion] | [buffer] [character]] |
---|
448 | * |
---|
449 | * and there are several special cases. The motion value is itself a vi |
---|
450 | * command, with the syntax: |
---|
451 | * |
---|
452 | * [count] key [character] |
---|
453 | */ |
---|
454 | static gcret_t |
---|
455 | v_cmd(sp, dp, vp, ismotion, comcountp, mappedp) |
---|
456 | SCR *sp; |
---|
457 | VICMD *dp, *vp; |
---|
458 | VICMD *ismotion; /* Previous key if getting motion component. */ |
---|
459 | int *comcountp, *mappedp; |
---|
460 | { |
---|
461 | enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart; |
---|
462 | EVENT ev; |
---|
463 | VIKEYS const *kp; |
---|
464 | gcret_t gcret; |
---|
465 | u_int flags; |
---|
466 | CHAR_T key; |
---|
467 | char *s; |
---|
468 | |
---|
469 | /* |
---|
470 | * Get a key. |
---|
471 | * |
---|
472 | * <escape> cancels partial commands, i.e. a command where at least |
---|
473 | * one non-numeric character has been entered. Otherwise, it beeps |
---|
474 | * the terminal. |
---|
475 | * |
---|
476 | * !!! |
---|
477 | * POSIX 1003.2-1992 explicitly disallows cancelling commands where |
---|
478 | * all that's been entered is a number, requiring that the terminal |
---|
479 | * be alerted. |
---|
480 | */ |
---|
481 | cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL; |
---|
482 | if ((gcret = |
---|
483 | v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) { |
---|
484 | if (gcret == GC_EVENT) |
---|
485 | vp->ev = ev; |
---|
486 | return (gcret); |
---|
487 | } |
---|
488 | if (ev.e_value == K_ESCAPE) |
---|
489 | goto esc; |
---|
490 | if (F_ISSET(&ev.e_ch, CH_MAPPED)) |
---|
491 | *mappedp = 1; |
---|
492 | key = ev.e_c; |
---|
493 | |
---|
494 | if (ismotion == NULL) |
---|
495 | cpart = NOTPARTIAL; |
---|
496 | |
---|
497 | /* Pick up optional buffer. */ |
---|
498 | if (key == '"') { |
---|
499 | cpart = ISPARTIAL; |
---|
500 | if (ismotion != NULL) { |
---|
501 | v_emsg(sp, NULL, VIM_COMBUF); |
---|
502 | return (GC_ERR); |
---|
503 | } |
---|
504 | KEY(vp->buffer, 0); |
---|
505 | F_SET(vp, VC_BUFFER); |
---|
506 | |
---|
507 | KEY(key, EC_MAPCOMMAND); |
---|
508 | } |
---|
509 | |
---|
510 | /* |
---|
511 | * Pick up optional count, where a leading 0 is not a count, |
---|
512 | * it's a command. |
---|
513 | */ |
---|
514 | if (isdigit(key) && key != '0') { |
---|
515 | if (v_count(sp, key, &vp->count)) |
---|
516 | return (GC_ERR); |
---|
517 | F_SET(vp, VC_C1SET); |
---|
518 | *comcountp = 1; |
---|
519 | |
---|
520 | KEY(key, EC_MAPCOMMAND); |
---|
521 | } else |
---|
522 | *comcountp = 0; |
---|
523 | |
---|
524 | /* Pick up optional buffer. */ |
---|
525 | if (key == '"') { |
---|
526 | cpart = ISPARTIAL; |
---|
527 | if (F_ISSET(vp, VC_BUFFER)) { |
---|
528 | msgq(sp, M_ERR, "234|Only one buffer may be specified"); |
---|
529 | return (GC_ERR); |
---|
530 | } |
---|
531 | if (ismotion != NULL) { |
---|
532 | v_emsg(sp, NULL, VIM_COMBUF); |
---|
533 | return (GC_ERR); |
---|
534 | } |
---|
535 | KEY(vp->buffer, 0); |
---|
536 | F_SET(vp, VC_BUFFER); |
---|
537 | |
---|
538 | KEY(key, EC_MAPCOMMAND); |
---|
539 | } |
---|
540 | |
---|
541 | /* Check for an OOB command key. */ |
---|
542 | cpart = ISPARTIAL; |
---|
543 | if (key > MAXVIKEY) { |
---|
544 | v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM); |
---|
545 | return (GC_ERR); |
---|
546 | } |
---|
547 | kp = &vikeys[vp->key = key]; |
---|
548 | |
---|
549 | /* |
---|
550 | * !!! |
---|
551 | * Historically, D accepted and then ignored a count. Match it. |
---|
552 | */ |
---|
553 | if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) { |
---|
554 | *comcountp = 0; |
---|
555 | vp->count = 0; |
---|
556 | F_CLR(vp, VC_C1SET); |
---|
557 | } |
---|
558 | |
---|
559 | /* Check for command aliases. */ |
---|
560 | if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL) |
---|
561 | return (GC_ERR); |
---|
562 | |
---|
563 | /* The tildeop option makes the ~ command take a motion. */ |
---|
564 | if (key == '~' && O_ISSET(sp, O_TILDEOP)) |
---|
565 | kp = &tmotion; |
---|
566 | |
---|
567 | vp->kp = kp; |
---|
568 | |
---|
569 | /* |
---|
570 | * Find the command. The only legal command with no underlying |
---|
571 | * function is dot. It's historic practice that <escape> doesn't |
---|
572 | * just erase the preceding number, it beeps the terminal as well. |
---|
573 | * It's a common problem, so just beep the terminal unless verbose |
---|
574 | * was set. |
---|
575 | */ |
---|
576 | if (kp->func == NULL) { |
---|
577 | if (key != '.') { |
---|
578 | v_emsg(sp, KEY_NAME(sp, key), |
---|
579 | ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM); |
---|
580 | return (GC_ERR); |
---|
581 | } |
---|
582 | |
---|
583 | /* If called for a motion command, stop now. */ |
---|
584 | if (dp == NULL) |
---|
585 | goto usage; |
---|
586 | |
---|
587 | /* |
---|
588 | * !!! |
---|
589 | * If a '.' is immediately entered after an undo command, we |
---|
590 | * replay the log instead of redoing the last command. This |
---|
591 | * is necessary because 'u' can't set the dot command -- see |
---|
592 | * vi/v_undo.c:v_undo for details. |
---|
593 | */ |
---|
594 | if (VIP(sp)->u_ccnt == sp->ccnt) { |
---|
595 | vp->kp = &vikeys['u']; |
---|
596 | F_SET(vp, VC_ISDOT); |
---|
597 | return (GC_OK); |
---|
598 | } |
---|
599 | |
---|
600 | /* Otherwise, a repeatable command must have been executed. */ |
---|
601 | if (!F_ISSET(dp, VC_ISDOT)) { |
---|
602 | msgq(sp, M_ERR, "208|No command to repeat"); |
---|
603 | return (GC_ERR); |
---|
604 | } |
---|
605 | |
---|
606 | /* Set new count/buffer, if any, and return. */ |
---|
607 | if (F_ISSET(vp, VC_C1SET)) { |
---|
608 | F_SET(dp, VC_C1SET); |
---|
609 | dp->count = vp->count; |
---|
610 | } |
---|
611 | if (F_ISSET(vp, VC_BUFFER)) |
---|
612 | dp->buffer = vp->buffer; |
---|
613 | |
---|
614 | *vp = *dp; |
---|
615 | return (GC_OK); |
---|
616 | } |
---|
617 | |
---|
618 | /* Set the flags based on the command flags. */ |
---|
619 | flags = kp->flags; |
---|
620 | |
---|
621 | /* Check for illegal count. */ |
---|
622 | if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT)) |
---|
623 | goto usage; |
---|
624 | |
---|
625 | /* Illegal motion command. */ |
---|
626 | if (ismotion == NULL) { |
---|
627 | /* Illegal buffer. */ |
---|
628 | if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER)) |
---|
629 | goto usage; |
---|
630 | |
---|
631 | /* Required buffer. */ |
---|
632 | if (LF_ISSET(V_RBUF)) { |
---|
633 | KEY(vp->buffer, 0); |
---|
634 | F_SET(vp, VC_BUFFER); |
---|
635 | } |
---|
636 | } |
---|
637 | |
---|
638 | /* |
---|
639 | * Special case: '[', ']' and 'Z' commands. Doesn't the fact that |
---|
640 | * the *single* characters don't mean anything but the *doubled* |
---|
641 | * characters do, just frost your shorts? |
---|
642 | */ |
---|
643 | if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') { |
---|
644 | /* |
---|
645 | * Historically, half entered [[, ]] or Z commands weren't |
---|
646 | * cancelled by <escape>, the terminal was beeped instead. |
---|
647 | * POSIX.2-1992 probably didn't notice, and requires that |
---|
648 | * they be cancelled instead of beeping. Seems fine to me. |
---|
649 | * |
---|
650 | * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular |
---|
651 | * vi meta-character, and we don't want the user to wait while |
---|
652 | * we time out a possible mapping. This *appears* to match |
---|
653 | * historic vi practice, but with mapping characters, you Just |
---|
654 | * Never Know. |
---|
655 | */ |
---|
656 | KEY(key, 0); |
---|
657 | |
---|
658 | if (vp->key != key) { |
---|
659 | usage: if (ismotion == NULL) |
---|
660 | s = kp->usage; |
---|
661 | else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP)) |
---|
662 | s = tmotion.usage; |
---|
663 | else |
---|
664 | s = vikeys[ismotion->key].usage; |
---|
665 | v_emsg(sp, s, VIM_USAGE); |
---|
666 | return (GC_ERR); |
---|
667 | } |
---|
668 | } |
---|
669 | /* Special case: 'z' command. */ |
---|
670 | if (vp->key == 'z') { |
---|
671 | KEY(vp->character, 0); |
---|
672 | if (isdigit(vp->character)) { |
---|
673 | if (v_count(sp, vp->character, &vp->count2)) |
---|
674 | return (GC_ERR); |
---|
675 | F_SET(vp, VC_C2SET); |
---|
676 | KEY(vp->character, 0); |
---|
677 | } |
---|
678 | } |
---|
679 | |
---|
680 | /* |
---|
681 | * Commands that have motion components can be doubled to |
---|
682 | * imply the current line. |
---|
683 | */ |
---|
684 | if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) { |
---|
685 | msgq(sp, M_ERR, "210|%s may not be used as a motion command", |
---|
686 | KEY_NAME(sp, key)); |
---|
687 | return (GC_ERR); |
---|
688 | } |
---|
689 | |
---|
690 | /* Required character. */ |
---|
691 | if (LF_ISSET(V_CHAR)) |
---|
692 | KEY(vp->character, 0); |
---|
693 | |
---|
694 | /* Get any associated cursor word. */ |
---|
695 | if (F_ISSET(kp, V_KEYW) && v_keyword(sp)) |
---|
696 | return (GC_ERR); |
---|
697 | |
---|
698 | return (GC_OK); |
---|
699 | |
---|
700 | esc: switch (cpart) { |
---|
701 | case COMMANDMODE: |
---|
702 | msgq(sp, M_BERR, "211|Already in command mode"); |
---|
703 | return (GC_ERR_NOFLUSH); |
---|
704 | case ISPARTIAL: |
---|
705 | break; |
---|
706 | case NOTPARTIAL: |
---|
707 | (void)sp->gp->scr_bell(sp); |
---|
708 | break; |
---|
709 | } |
---|
710 | return (GC_ERR); |
---|
711 | } |
---|
712 | |
---|
713 | /* |
---|
714 | * v_motion -- |
---|
715 | * |
---|
716 | * Get resulting motion mark. |
---|
717 | */ |
---|
718 | static int |
---|
719 | v_motion(sp, dm, vp, mappedp) |
---|
720 | SCR *sp; |
---|
721 | VICMD *dm, *vp; |
---|
722 | int *mappedp; |
---|
723 | { |
---|
724 | VICMD motion; |
---|
725 | size_t len; |
---|
726 | u_long cnt; |
---|
727 | u_int flags; |
---|
728 | int tilde_reset, notused; |
---|
729 | |
---|
730 | /* |
---|
731 | * If '.' command, use the dot motion, else get the motion command. |
---|
732 | * Clear any line motion flags, the subsequent motion isn't always |
---|
733 | * the same, i.e. "/aaa" may or may not be a line motion. |
---|
734 | */ |
---|
735 | if (F_ISSET(vp, VC_ISDOT)) { |
---|
736 | motion = *dm; |
---|
737 | F_SET(&motion, VC_ISDOT); |
---|
738 | F_CLR(&motion, VM_COMMASK); |
---|
739 | } else { |
---|
740 | memset(&motion, 0, sizeof(VICMD)); |
---|
741 | if (v_cmd(sp, NULL, &motion, vp, ¬used, mappedp) != GC_OK) |
---|
742 | return (1); |
---|
743 | } |
---|
744 | |
---|
745 | /* |
---|
746 | * A count may be provided both to the command and to the motion, in |
---|
747 | * which case the count is multiplicative. For example, "3y4y" is the |
---|
748 | * same as "12yy". This count is provided to the motion command and |
---|
749 | * not to the regular function. |
---|
750 | */ |
---|
751 | cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1; |
---|
752 | if (F_ISSET(vp, VC_C1SET)) { |
---|
753 | motion.count *= vp->count; |
---|
754 | F_SET(&motion, VC_C1SET); |
---|
755 | |
---|
756 | /* |
---|
757 | * Set flags to restore the original values of the command |
---|
758 | * structure so dot commands can change the count values, |
---|
759 | * e.g. "2dw" "3." deletes a total of five words. |
---|
760 | */ |
---|
761 | F_CLR(vp, VC_C1SET); |
---|
762 | F_SET(vp, VC_C1RESET); |
---|
763 | } |
---|
764 | |
---|
765 | /* |
---|
766 | * Some commands can be repeated to indicate the current line. In |
---|
767 | * this case, or if the command is a "line command", set the flags |
---|
768 | * appropriately. If not a doubled command, run the function to get |
---|
769 | * the resulting mark. |
---|
770 | */ |
---|
771 | if (vp->key == motion.key) { |
---|
772 | F_SET(vp, VM_LDOUBLE | VM_LMODE); |
---|
773 | |
---|
774 | /* Set the origin of the command. */ |
---|
775 | vp->m_start.lno = sp->lno; |
---|
776 | vp->m_start.cno = 0; |
---|
777 | |
---|
778 | /* |
---|
779 | * Set the end of the command. |
---|
780 | * |
---|
781 | * If the current line is missing, i.e. the file is empty, |
---|
782 | * historic vi permitted a "cc" or "!!" command to insert |
---|
783 | * text. |
---|
784 | */ |
---|
785 | vp->m_stop.lno = sp->lno + motion.count - 1; |
---|
786 | if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) { |
---|
787 | if (vp->m_stop.lno != 1 || |
---|
788 | vp->key != 'c' && vp->key != '!') { |
---|
789 | v_emsg(sp, NULL, VIM_EMPTY); |
---|
790 | return (1); |
---|
791 | } |
---|
792 | vp->m_stop.cno = 0; |
---|
793 | } else |
---|
794 | vp->m_stop.cno = len ? len - 1 : 0; |
---|
795 | } else { |
---|
796 | /* |
---|
797 | * Motion commands change the underlying movement (*snarl*). |
---|
798 | * For example, "l" is illegal at the end of a line, but "dl" |
---|
799 | * is not. Set flags so the function knows the situation. |
---|
800 | */ |
---|
801 | motion.rkp = vp->kp; |
---|
802 | |
---|
803 | /* |
---|
804 | * XXX |
---|
805 | * Use yank instead of creating a new motion command, it's a |
---|
806 | * lot easier for now. |
---|
807 | */ |
---|
808 | if (vp->kp == &tmotion) { |
---|
809 | tilde_reset = 1; |
---|
810 | vp->kp = &vikeys['y']; |
---|
811 | } else |
---|
812 | tilde_reset = 0; |
---|
813 | |
---|
814 | /* |
---|
815 | * Copy the key flags into the local structure, except for the |
---|
816 | * RCM flags -- the motion command will set the RCM flags in |
---|
817 | * the vp structure if necessary. This means that the motion |
---|
818 | * command is expected to determine where the cursor ends up! |
---|
819 | * However, we save off the current RCM mask and restore it if |
---|
820 | * it no RCM flags are set by the motion command, with a small |
---|
821 | * modification. |
---|
822 | * |
---|
823 | * We replace the VM_RCM_SET flag with the VM_RCM flag. This |
---|
824 | * is so that cursor movement doesn't set the relative position |
---|
825 | * unless the motion command explicitly specified it. This |
---|
826 | * appears to match historic practice, but I've never been able |
---|
827 | * to develop a hard-and-fast rule. |
---|
828 | */ |
---|
829 | flags = F_ISSET(vp, VM_RCM_MASK); |
---|
830 | if (LF_ISSET(VM_RCM_SET)) { |
---|
831 | LF_SET(VM_RCM); |
---|
832 | LF_CLR(VM_RCM_SET); |
---|
833 | } |
---|
834 | F_CLR(vp, VM_RCM_MASK); |
---|
835 | F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK); |
---|
836 | |
---|
837 | /* |
---|
838 | * Set the three cursor locations to the current cursor. This |
---|
839 | * permits commands like 'j' and 'k', that are line oriented |
---|
840 | * motions and have special cursor suck semantics when they are |
---|
841 | * used as standalone commands, to ignore column positioning. |
---|
842 | */ |
---|
843 | motion.m_final.lno = |
---|
844 | motion.m_stop.lno = motion.m_start.lno = sp->lno; |
---|
845 | motion.m_final.cno = |
---|
846 | motion.m_stop.cno = motion.m_start.cno = sp->cno; |
---|
847 | |
---|
848 | /* Run the function. */ |
---|
849 | if ((motion.kp->func)(sp, &motion)) |
---|
850 | return (1); |
---|
851 | |
---|
852 | /* |
---|
853 | * If the current line is missing, i.e. the file is empty, |
---|
854 | * historic vi allowed "c<motion>" or "!<motion>" to insert |
---|
855 | * text. Otherwise fail -- most motion commands will have |
---|
856 | * already failed, but some, e.g. G, succeed in empty files. |
---|
857 | */ |
---|
858 | if (!db_exist(sp, vp->m_stop.lno)) { |
---|
859 | if (vp->m_stop.lno != 1 || |
---|
860 | vp->key != 'c' && vp->key != '!') { |
---|
861 | v_emsg(sp, NULL, VIM_EMPTY); |
---|
862 | return (1); |
---|
863 | } |
---|
864 | vp->m_stop.cno = 0; |
---|
865 | } |
---|
866 | |
---|
867 | /* |
---|
868 | * XXX |
---|
869 | * See above. |
---|
870 | */ |
---|
871 | if (tilde_reset) |
---|
872 | vp->kp = &tmotion; |
---|
873 | |
---|
874 | /* |
---|
875 | * Copy cut buffer, line mode and cursor position information |
---|
876 | * from the motion command structure, i.e. anything that the |
---|
877 | * motion command can set for us. The commands can flag the |
---|
878 | * movement as a line motion (see v_sentence) as well as set |
---|
879 | * the VM_RCM_* flags explicitly. |
---|
880 | */ |
---|
881 | F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK)); |
---|
882 | |
---|
883 | /* |
---|
884 | * If the motion command set no relative motion flags, use |
---|
885 | * the (slightly) modified previous values. |
---|
886 | */ |
---|
887 | if (!F_ISSET(vp, VM_RCM_MASK)) |
---|
888 | F_SET(vp, flags); |
---|
889 | |
---|
890 | /* |
---|
891 | * Commands can change behaviors based on the motion command |
---|
892 | * used, for example, the ! command repeated the last bang |
---|
893 | * command if N or n was used as the motion. |
---|
894 | */ |
---|
895 | vp->rkp = motion.kp; |
---|
896 | |
---|
897 | /* |
---|
898 | * Motion commands can reset all of the cursor information. |
---|
899 | * If the motion is in the reverse direction, switch the |
---|
900 | * from and to MARK's so that it's in a forward direction. |
---|
901 | * Motions are from the from MARK to the to MARK (inclusive). |
---|
902 | */ |
---|
903 | if (motion.m_start.lno > motion.m_stop.lno || |
---|
904 | motion.m_start.lno == motion.m_stop.lno && |
---|
905 | motion.m_start.cno > motion.m_stop.cno) { |
---|
906 | vp->m_start = motion.m_stop; |
---|
907 | vp->m_stop = motion.m_start; |
---|
908 | } else { |
---|
909 | vp->m_start = motion.m_start; |
---|
910 | vp->m_stop = motion.m_stop; |
---|
911 | } |
---|
912 | vp->m_final = motion.m_final; |
---|
913 | } |
---|
914 | |
---|
915 | /* |
---|
916 | * If the command sets dot, save the motion structure. The motion |
---|
917 | * count was changed above and needs to be reset, that's why this |
---|
918 | * is done here, and not in the calling routine. |
---|
919 | */ |
---|
920 | if (F_ISSET(vp->kp, V_DOT)) { |
---|
921 | *dm = motion; |
---|
922 | dm->count = cnt; |
---|
923 | } |
---|
924 | return (0); |
---|
925 | } |
---|
926 | |
---|
927 | /* |
---|
928 | * v_init -- |
---|
929 | * Initialize the vi screen. |
---|
930 | */ |
---|
931 | static int |
---|
932 | v_init(sp) |
---|
933 | SCR *sp; |
---|
934 | { |
---|
935 | GS *gp; |
---|
936 | VI_PRIVATE *vip; |
---|
937 | |
---|
938 | gp = sp->gp; |
---|
939 | vip = VIP(sp); |
---|
940 | |
---|
941 | /* Switch into vi. */ |
---|
942 | if (gp->scr_screen(sp, SC_VI)) |
---|
943 | return (1); |
---|
944 | (void)gp->scr_attr(sp, SA_ALTERNATE, 1); |
---|
945 | |
---|
946 | F_CLR(sp, SC_EX | SC_SCR_EX); |
---|
947 | F_SET(sp, SC_VI); |
---|
948 | |
---|
949 | /* |
---|
950 | * Initialize screen values. |
---|
951 | * |
---|
952 | * Small windows: see vs_refresh(), section 6a. |
---|
953 | * |
---|
954 | * Setup: |
---|
955 | * t_minrows is the minimum rows to display |
---|
956 | * t_maxrows is the maximum rows to display (rows - 1) |
---|
957 | * t_rows is the rows currently being displayed |
---|
958 | */ |
---|
959 | sp->rows = vip->srows = O_VAL(sp, O_LINES); |
---|
960 | sp->cols = O_VAL(sp, O_COLUMNS); |
---|
961 | sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW); |
---|
962 | if (sp->rows != 1) { |
---|
963 | if (sp->t_rows > sp->rows - 1) { |
---|
964 | sp->t_minrows = sp->t_rows = sp->rows - 1; |
---|
965 | msgq(sp, M_INFO, |
---|
966 | "214|Windows option value is too large, max is %u", |
---|
967 | sp->t_rows); |
---|
968 | } |
---|
969 | sp->t_maxrows = sp->rows - 1; |
---|
970 | } else |
---|
971 | sp->t_maxrows = 1; |
---|
972 | sp->woff = 0; |
---|
973 | |
---|
974 | /* Create a screen map. */ |
---|
975 | CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP)); |
---|
976 | TMAP = HMAP + (sp->t_rows - 1); |
---|
977 | HMAP->lno = sp->lno; |
---|
978 | HMAP->coff = 0; |
---|
979 | HMAP->soff = 1; |
---|
980 | |
---|
981 | /* |
---|
982 | * Fill the screen map from scratch -- try and center the line. That |
---|
983 | * way if we're starting with a file we've seen before, we'll put the |
---|
984 | * line in the middle, otherwise, it won't work and we'll end up with |
---|
985 | * the line at the top. |
---|
986 | */ |
---|
987 | F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER); |
---|
988 | |
---|
989 | /* Invalidate the cursor. */ |
---|
990 | F_SET(vip, VIP_CUR_INVALID); |
---|
991 | |
---|
992 | /* Paint the screen image from scratch. */ |
---|
993 | F_SET(vip, VIP_N_EX_PAINT); |
---|
994 | |
---|
995 | return (0); |
---|
996 | } |
---|
997 | |
---|
998 | /* |
---|
999 | * v_dtoh -- |
---|
1000 | * Move all but the current screen to the hidden queue. |
---|
1001 | */ |
---|
1002 | static void |
---|
1003 | v_dtoh(sp) |
---|
1004 | SCR *sp; |
---|
1005 | { |
---|
1006 | GS *gp; |
---|
1007 | SCR *tsp; |
---|
1008 | int hidden; |
---|
1009 | |
---|
1010 | /* Move all screens to the hidden queue, tossing screen maps. */ |
---|
1011 | for (hidden = 0, gp = sp->gp; |
---|
1012 | (tsp = gp->dq.cqh_first) != (void *)&gp->dq; ++hidden) { |
---|
1013 | if (_HMAP(tsp) != NULL) { |
---|
1014 | free(_HMAP(tsp)); |
---|
1015 | _HMAP(tsp) = NULL; |
---|
1016 | } |
---|
1017 | CIRCLEQ_REMOVE(&gp->dq, tsp, q); |
---|
1018 | CIRCLEQ_INSERT_TAIL(&gp->hq, tsp, q); |
---|
1019 | } |
---|
1020 | |
---|
1021 | /* Move current screen back to the display queue. */ |
---|
1022 | CIRCLEQ_REMOVE(&gp->hq, sp, q); |
---|
1023 | CIRCLEQ_INSERT_TAIL(&gp->dq, sp, q); |
---|
1024 | |
---|
1025 | /* |
---|
1026 | * XXX |
---|
1027 | * Don't bother internationalizing this message, it's going to |
---|
1028 | * go away as soon as we have one-line screens. --TK |
---|
1029 | */ |
---|
1030 | if (hidden > 1) |
---|
1031 | msgq(sp, M_INFO, |
---|
1032 | "%d screens backgrounded; use :display to list them", |
---|
1033 | hidden - 1); |
---|
1034 | } |
---|
1035 | |
---|
1036 | /* |
---|
1037 | * v_keyword -- |
---|
1038 | * Get the word (or non-word) the cursor is on. |
---|
1039 | */ |
---|
1040 | static int |
---|
1041 | v_keyword(sp) |
---|
1042 | SCR *sp; |
---|
1043 | { |
---|
1044 | VI_PRIVATE *vip; |
---|
1045 | size_t beg, end, len; |
---|
1046 | int moved, state; |
---|
1047 | char *p; |
---|
1048 | |
---|
1049 | if (db_get(sp, sp->lno, DBG_FATAL, &p, &len)) |
---|
1050 | return (1); |
---|
1051 | |
---|
1052 | /* |
---|
1053 | * !!! |
---|
1054 | * Historically, tag commands skipped over any leading whitespace |
---|
1055 | * characters. Make this true in general when using cursor words. |
---|
1056 | * If movement, getting a cursor word implies moving the cursor to |
---|
1057 | * its beginning. Refresh now. |
---|
1058 | * |
---|
1059 | * !!! |
---|
1060 | * Find the beginning/end of the keyword. Keywords are currently |
---|
1061 | * used for cursor-word searching and for tags. Historical vi |
---|
1062 | * only used the word in a tag search from the cursor to the end |
---|
1063 | * of the word, i.e. if the cursor was on the 'b' in " abc ", the |
---|
1064 | * tag was "bc". For consistency, we make cursor word searches |
---|
1065 | * follow the same rule. |
---|
1066 | */ |
---|
1067 | for (moved = 0, |
---|
1068 | beg = sp->cno; beg < len && isspace(p[beg]); moved = 1, ++beg); |
---|
1069 | if (beg >= len) { |
---|
1070 | msgq(sp, M_BERR, "212|Cursor not in a word"); |
---|
1071 | return (1); |
---|
1072 | } |
---|
1073 | if (moved) { |
---|
1074 | sp->cno = beg; |
---|
1075 | (void)vs_refresh(sp, 0); |
---|
1076 | } |
---|
1077 | |
---|
1078 | /* Find the end of the word. */ |
---|
1079 | for (state = inword(p[beg]), |
---|
1080 | end = beg; ++end < len && state == inword(p[end]);); |
---|
1081 | |
---|
1082 | vip = VIP(sp); |
---|
1083 | len = (end - beg); |
---|
1084 | BINC_RET(sp, vip->keyw, vip->klen, len); |
---|
1085 | memmove(vip->keyw, p + beg, len); |
---|
1086 | vip->keyw[len] = '\0'; /* XXX */ |
---|
1087 | return (0); |
---|
1088 | } |
---|
1089 | |
---|
1090 | /* |
---|
1091 | * v_alias -- |
---|
1092 | * Check for a command alias. |
---|
1093 | */ |
---|
1094 | static VIKEYS const * |
---|
1095 | v_alias(sp, vp, kp) |
---|
1096 | SCR *sp; |
---|
1097 | VICMD *vp; |
---|
1098 | VIKEYS const *kp; |
---|
1099 | { |
---|
1100 | CHAR_T push; |
---|
1101 | |
---|
1102 | switch (vp->key) { |
---|
1103 | case 'C': /* C -> c$ */ |
---|
1104 | push = '$'; |
---|
1105 | vp->key = 'c'; |
---|
1106 | break; |
---|
1107 | case 'D': /* D -> d$ */ |
---|
1108 | push = '$'; |
---|
1109 | vp->key = 'd'; |
---|
1110 | break; |
---|
1111 | case 'S': /* S -> c_ */ |
---|
1112 | push = '_'; |
---|
1113 | vp->key = 'c'; |
---|
1114 | break; |
---|
1115 | case 'Y': /* Y -> y_ */ |
---|
1116 | push = '_'; |
---|
1117 | vp->key = 'y'; |
---|
1118 | break; |
---|
1119 | default: |
---|
1120 | return (kp); |
---|
1121 | } |
---|
1122 | return (v_event_push(sp, |
---|
1123 | NULL, &push, 1, CH_NOMAP | CH_QUOTED) ? NULL : &vikeys[vp->key]); |
---|
1124 | } |
---|
1125 | |
---|
1126 | /* |
---|
1127 | * v_count -- |
---|
1128 | * Return the next count. |
---|
1129 | */ |
---|
1130 | static int |
---|
1131 | v_count(sp, fkey, countp) |
---|
1132 | SCR *sp; |
---|
1133 | ARG_CHAR_T fkey; |
---|
1134 | u_long *countp; |
---|
1135 | { |
---|
1136 | EVENT ev; |
---|
1137 | u_long count, tc; |
---|
1138 | |
---|
1139 | ev.e_c = fkey; |
---|
1140 | count = tc = 0; |
---|
1141 | do { |
---|
1142 | /* |
---|
1143 | * XXX |
---|
1144 | * Assume that overflow results in a smaller number. |
---|
1145 | */ |
---|
1146 | tc = count * 10 + ev.e_c - '0'; |
---|
1147 | if (count > tc) { |
---|
1148 | /* Toss to the next non-digit. */ |
---|
1149 | do { |
---|
1150 | if (v_key(sp, 0, &ev, |
---|
1151 | EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK) |
---|
1152 | return (1); |
---|
1153 | } while (isdigit(ev.e_c)); |
---|
1154 | msgq(sp, M_ERR, |
---|
1155 | "235|Number larger than %lu", ULONG_MAX); |
---|
1156 | return (1); |
---|
1157 | } |
---|
1158 | count = tc; |
---|
1159 | if (v_key(sp, 0, &ev, EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK) |
---|
1160 | return (1); |
---|
1161 | } while (isdigit(ev.e_c)); |
---|
1162 | *countp = count; |
---|
1163 | return (0); |
---|
1164 | } |
---|
1165 | |
---|
1166 | /* |
---|
1167 | * v_key -- |
---|
1168 | * Return the next event. |
---|
1169 | */ |
---|
1170 | static gcret_t |
---|
1171 | v_key(sp, command_events, evp, ec_flags) |
---|
1172 | SCR *sp; |
---|
1173 | int command_events; |
---|
1174 | EVENT *evp; |
---|
1175 | u_int32_t ec_flags; |
---|
1176 | { |
---|
1177 | u_int32_t quote; |
---|
1178 | |
---|
1179 | for (quote = 0;;) { |
---|
1180 | if (v_event_get(sp, evp, 0, ec_flags | quote)) |
---|
1181 | return (GC_FATAL); |
---|
1182 | quote = 0; |
---|
1183 | |
---|
1184 | switch (evp->e_event) { |
---|
1185 | case E_CHARACTER: |
---|
1186 | /* |
---|
1187 | * !!! |
---|
1188 | * Historically, ^V was ignored in the command stream, |
---|
1189 | * although it had a useful side-effect of interrupting |
---|
1190 | * mappings. Adding a quoting bit to the call probably |
---|
1191 | * extends historic practice, but it feels right. |
---|
1192 | */ |
---|
1193 | if (evp->e_value == K_VLNEXT) { |
---|
1194 | quote = EC_QUOTED; |
---|
1195 | break; |
---|
1196 | } |
---|
1197 | return (GC_OK); |
---|
1198 | case E_ERR: |
---|
1199 | case E_EOF: |
---|
1200 | return (GC_FATAL); |
---|
1201 | case E_INTERRUPT: |
---|
1202 | /* |
---|
1203 | * !!! |
---|
1204 | * Historically, vi beeped on command level interrupts. |
---|
1205 | * |
---|
1206 | * Historically, vi exited to ex mode if no file was |
---|
1207 | * named on the command line, and two interrupts were |
---|
1208 | * generated in a row. (Just figured you might want |
---|
1209 | * to know that.) |
---|
1210 | */ |
---|
1211 | (void)sp->gp->scr_bell(sp); |
---|
1212 | return (GC_INTERRUPT); |
---|
1213 | case E_REPAINT: |
---|
1214 | if (vs_repaint(sp, evp)) |
---|
1215 | return (GC_FATAL); |
---|
1216 | break; |
---|
1217 | case E_WRESIZE: |
---|
1218 | return (GC_ERR); |
---|
1219 | case E_QUIT: |
---|
1220 | case E_WRITE: |
---|
1221 | if (command_events) |
---|
1222 | return (GC_EVENT); |
---|
1223 | /* FALLTHROUGH */ |
---|
1224 | default: |
---|
1225 | v_event_err(sp, evp); |
---|
1226 | return (GC_ERR); |
---|
1227 | } |
---|
1228 | } |
---|
1229 | /* NOTREACHED */ |
---|
1230 | } |
---|
1231 | |
---|
1232 | #if defined(DEBUG) && defined(COMLOG) |
---|
1233 | /* |
---|
1234 | * v_comlog -- |
---|
1235 | * Log the contents of the command structure. |
---|
1236 | */ |
---|
1237 | static void |
---|
1238 | v_comlog(sp, vp) |
---|
1239 | SCR *sp; |
---|
1240 | VICMD *vp; |
---|
1241 | { |
---|
1242 | TRACE(sp, "vcmd: %c", vp->key); |
---|
1243 | if (F_ISSET(vp, VC_BUFFER)) |
---|
1244 | TRACE(sp, " buffer: %c", vp->buffer); |
---|
1245 | if (F_ISSET(vp, VC_C1SET)) |
---|
1246 | TRACE(sp, " c1: %lu", vp->count); |
---|
1247 | if (F_ISSET(vp, VC_C2SET)) |
---|
1248 | TRACE(sp, " c2: %lu", vp->count2); |
---|
1249 | TRACE(sp, " flags: 0x%x\n", vp->flags); |
---|
1250 | } |
---|
1251 | #endif |
---|