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