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