2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: vi.c,v 8.44 1994/01/08 16:40:34 bostic Exp $ (Berkeley) $Date: 1994/01/08 16:40:34 $";
12 #include <sys/types.h>
22 static int getcmd
__P((SCR
*, EXF
*,
23 VICMDARG
*, VICMDARG
*, VICMDARG
*, int *));
25 getcount
__P((SCR
*, ARG_CHAR_T
, u_long
*));
27 getkey
__P((SCR
*, CH
*, u_int
));
28 static int getkeyword
__P((SCR
*, EXF
*, VICMDARG
*, u_int
));
29 static int getmotion
__P((SCR
*, EXF
*,
30 VICMDARG
*, VICMDARG
*, MARK
*, MARK
*));
34 * The dot structure can be set by the underlying vi functions,
35 * see v_Put() and v_put().
37 #define DOT (&VIP(sp)->sdot)
38 #define DOTMOTION (&VIP(sp)->sdotmotion)
42 * Main vi command loop.
51 u_int flags
, saved_mode
;
58 /* Paint the screen. */
59 if (sp
->s_refresh(sp
, ep
)) {
64 /* Command initialization. */
65 memset(&cmd
, 0, sizeof(VICMDARG
));
67 for (eval
= 0, vp
= &cmd
;;) {
68 if (!MAPPED_KEYS_WAITING(sp
) && log_cursor(sp
, ep
))
72 * We get a command, which may or may not have an associated
73 * motion. If it does, we get it too, calling its underlying
74 * function to get the resulting mark. We then call the
75 * command setting the cursor to the resulting mark.
77 if (getcmd(sp
, ep
, DOT
, vp
, NULL
, &comcount
))
81 * Historical practice: if a dot command gets a new count,
82 * any motion component goes away, i.e. "d3w2." deletes a
85 if (F_ISSET(vp
, VC_ISDOT
) && comcount
)
88 /* Get any associated keyword. */
89 flags
= vp
->kp
->flags
;
90 if (LF_ISSET(V_KEYNUM
| V_KEYW
) &&
91 getkeyword(sp
, ep
, vp
, flags
))
94 /* If a non-relative movement, copy the future absolute mark. */
95 if (LF_ISSET(V_ABS
)) {
101 * Do any required motion; getmotion sets the from MARK
102 * and the line mode flag.
104 if (LF_ISSET(V_MOTION
)) {
105 if (getmotion(sp
, ep
, DOTMOTION
, vp
, &fm
, &tm
))
109 * Set everything to the current cursor position.
110 * Line commands (ex: Y) default to the current line.
112 tm
.lno
= fm
.lno
= sp
->lno
;
113 tm
.cno
= fm
.cno
= sp
->cno
;
116 * Set line mode flag, for example, "yy".
118 * If a count is set, we set the to MARK here relative
119 * to the cursor/from MARK. This is done for commands
120 * that take both counts and motions, i.e. "4yy" and
121 * "y%" -- there's no way the command can known which
122 * the user did, so we have to do it here. There are
123 * other commands that are line mode commands and take
124 * counts ("#G", "#H") and for which this calculation
125 * is either meaningless or wrong. Each command must
126 * do its own validity checking of the value.
128 if (F_ISSET(vp
->kp
, V_LMODE
)) {
130 if (F_ISSET(vp
, VC_C1SET
)) {
131 tm
.lno
= sp
->lno
+ vp
->count
- 1;
137 /* Increment the command count. */
141 * Call the function. Set the return cursor to the current
142 * cursor position first -- the underlying routines don't
143 * bother to do the work if it doesn't move.
147 saved_mode
= F_ISSET(sp
, S_SCREENS
| S_MAJOR_CHANGE
);
148 if ((vp
->kp
->func
)(sp
, ep
, vp
, &fm
, &tm
, &m
))
151 /* Make sure no function left the temporary space locked. */
152 if (F_ISSET(sp
->gp
, G_TMP_INUSE
)) {
154 "Error: vi: temporary buffer not released.");
159 * If that command took us out of vi or changed the screen,
160 * then exit the loop without further action.
162 if (saved_mode
!= F_ISSET(sp
, S_SCREENS
| S_MAJOR_CHANGE
))
165 /* Set the absolute mark. */
166 if (LF_ISSET(V_ABS
) && mark_set(sp
, ep
, ABSMARK1
, &abs
, 1))
169 /* Set the dot command structure. */
170 if (LF_ISSET(V_DOT
)) {
172 F_SET(DOT
, VC_ISDOT
);
174 * If a count was supplied for both the command and
175 * its motion, the count was used only for the motion.
176 * Turn the count back on for the dot structure.
178 if (F_ISSET(vp
, VC_C1RESET
))
179 F_SET(DOT
, VC_C1SET
);
183 * Some vi row movements are "attracted" to the last position
184 * set, i.e. the V_RCM commands are moths to the V_RCM_SET
185 * commands' candle. It's broken into two parts. Here we deal
186 * with the command flags. In sp->relative(), we deal with the
187 * screen flags. If the movement is to the EOL the vi command
188 * handles it. If it's to the beginning, we handle it here.
190 * Note, some commands (e.g. _, ^) don't set the V_RCM_SETFNB
191 * flag, but do the work themselves. The reason is that they
192 * have to modify the column in case they're being used as a
193 * motion component. Other similar commands (e.g. +, -) don't
194 * have to modify the column because they are always line mode
195 * operations when used as motions, so the column number isn't
198 * Does this totally violate the screen and editor layering?
199 * You betcha. As they say, if you think you understand it,
202 switch (LF_ISSET(V_RCM
| V_RCM_SETFNB
|
203 V_RCM_SETLAST
| V_RCM_SETLFNB
| V_RCM_SETNNB
)) {
207 m
.cno
= sp
->s_relative(sp
, ep
, m
.lno
);
210 sp
->rcmflags
= RCM_LAST
;
213 if (fm
.lno
!= m
.lno
) {
214 if (nonblank(sp
, ep
, m
.lno
, &m
.cno
))
216 sp
->rcmflags
= RCM_FNB
;
223 if (nonblank(sp
, ep
, m
.lno
, &m
.cno
))
225 sp
->rcmflags
= RCM_FNB
;
231 /* Update the cursor. */
235 if (!MAPPED_KEYS_WAITING(sp
)) {
236 (void)msg_rpt(sp
, 1);
239 err
: term_map_flush(sp
, "Vi error");
242 /* Refresh the screen. */
243 if (sp
->s_refresh(sp
, ep
)) {
248 /* Set the new favorite position. */
249 if (LF_ISSET(V_RCM_SET
)) {
251 (void)sp
->s_column(sp
, ep
, &sp
->rcm
);
255 return (v_end(sp
) || eval
);
258 #define KEY(key, map) { \
259 if (getkey(sp, &ikey, map)) \
267 * The command structure for vi is less complex than ex (and don't think
268 * I'm not grateful!) The command syntax is:
270 * [count] [buffer] [count] key [[motion] | [buffer] [character]]
272 * and there are several special cases. The motion value is itself a vi
273 * command, with the syntax:
275 * [count] key [character]
278 getcmd(sp
, ep
, dp
, vp
, ismotion
, comcountp
)
282 VICMDARG
*ismotion
; /* Previous key if getting motion component. */
290 /* Refresh the command structure. */
291 memset(&vp
->vp_startzero
, 0,
292 (char *)&vp
->vp_endzero
- (char *)&vp
->vp_startzero
);
294 /* An escape bells the user if in command mode. */
295 if (getkey(sp
, &ikey
, TXT_MAPCOMMAND
)) {
296 if (ikey
.value
== K_ESCAPE
&& ismotion
== NULL
)
297 msgq(sp
, M_BERR
, "Already in command mode");
302 if (key
> MAXVIKEY
) {
303 msgq(sp
, M_BERR
, "%s isn't a vi command", charname(sp
, key
));
307 /* Pick up optional buffer. */
310 F_SET(vp
, VC_BUFFER
);
311 KEY(key
, TXT_MAPCOMMAND
);
315 * Pick up optional count, where a leading 0 is not a count,
318 if (isdigit(key
) && key
!= '0') {
319 if (getcount(sp
, key
, &vp
->count
))
323 KEY(key
, TXT_MAPCOMMAND
);
327 /* Pick up optional buffer. */
329 if (F_ISSET(vp
, VC_BUFFER
)) {
330 msgq(sp
, M_ERR
, "Only one buffer can be specified.");
334 F_SET(vp
, VC_BUFFER
);
335 KEY(key
, TXT_MAPCOMMAND
);
339 * Find the command. The only legal command with no underlying
342 kp
= vp
->kp
= &vikeys
[vp
->key
= key
];
343 if (kp
->func
== NULL
) {
346 "%s isn't a command", charname(sp
, key
));
350 /* If called for a motion command, stop now. */
354 /* A repeatable command must have been executed. */
355 if (!F_ISSET(dp
, VC_ISDOT
)) {
356 msgq(sp
, M_ERR
, "No command to repeat.");
362 * If a '.' is immediately entered after an undo command, we
363 * replay the log instead of redoing the last command. This
364 * is necessary because 'u' can't set the dot command -- see
365 * vi/v_undo.c:v_undo for details.
367 if (VIP(sp
)->u_ccnt
== sp
->ccnt
) {
368 vp
->kp
= &vikeys
['u'];
373 /* Set new count/buffer, if any, and return. */
374 if (F_ISSET(vp
, VC_C1SET
)) {
376 dp
->count
= vp
->count
;
378 if (F_ISSET(vp
, VC_BUFFER
))
379 dp
->buffer
= vp
->buffer
;
386 /* Check for illegal count. */
387 if (F_ISSET(vp
, VC_C1SET
) && !LF_ISSET(V_CNT
))
390 /* Illegal motion command. */
391 if (ismotion
== NULL
) {
392 /* Illegal buffer. */
393 if (!LF_ISSET(V_OBUF
) && F_ISSET(vp
, VC_BUFFER
))
396 /* Required buffer. */
397 if (LF_ISSET(V_RBUF
))
401 * Special case: '[', ']' and 'Z' commands. Doesn't the
402 * fact that the *single* characters don't mean anything
403 * but the *doubled* characters do just frost your shorts?
405 if (vp
->key
== '[' || vp
->key
== ']' || vp
->key
== 'Z') {
406 KEY(key
, TXT_MAPCOMMAND
);
410 /* Special case: 'z' command. */
411 if (vp
->key
== 'z') {
412 KEY(vp
->character
, 0);
413 if (isdigit(vp
->character
)) {
414 if (getcount(sp
, vp
->character
, &vp
->count2
))
417 KEY(vp
->character
, 0);
423 * Commands that have motion components can be doubled to
424 * imply the current line.
426 else if (ismotion
->key
!= key
&& !LF_ISSET(V_MOVE
)) {
427 usage
: msgq(sp
, M_ERR
, "Usage: %s", ismotion
!= NULL
?
428 vikeys
[ismotion
->key
].usage
: kp
->usage
);
432 /* Required character. */
433 if (LF_ISSET(V_CHAR
))
434 KEY(vp
->character
, 0);
442 * Get resulting motion mark.
445 getmotion(sp
, ep
, dm
, vp
, fm
, tm
)
456 /* If '.' command, use the dot motion, else get the motion command. */
457 if (F_ISSET(vp
, VC_ISDOT
)) {
459 F_SET(&motion
, VC_ISDOT
);
460 } else if (getcmd(sp
, ep
, NULL
, &motion
, vp
, ¬used
))
464 * A count may be provided both to the command and to the motion, in
465 * which case the count is multiplicative. For example, "3y4y" is the
466 * same as "12yy". This count is provided to the motion command and
467 * not to the regular function.
469 cnt
= motion
.count
= F_ISSET(&motion
, VC_C1SET
) ? motion
.count
: 1;
470 if (F_ISSET(vp
, VC_C1SET
)) {
471 motion
.count
*= vp
->count
;
472 F_SET(&motion
, VC_C1SET
);
475 * Set flags to restore the original values of the command
476 * structure so dot commands can change the count values,
477 * e.g. "2dw" "3." deletes a total of five words.
480 F_SET(vp
, VC_C1RESET
);
484 * Some commands can be repeated to indicate the current line. In
485 * this case, or if the command is a "line command", set the flags
486 * appropriately. If not a doubled command, run the function to get
487 * the resulting mark.
489 if (vp
->key
== motion
.key
) {
493 * Set the end of the command; the column is after the line.
495 * If the current line is missing, i.e. the file is empty,
496 * historic vi permitted a "cc" or "!!" command to insert
499 tm
->lno
= sp
->lno
+ motion
.count
- 1;
500 if (file_gline(sp
, ep
, tm
->lno
, &tm
->cno
) == NULL
) {
501 if (tm
->lno
!= 1 || vp
->key
!= 'c' && vp
->key
!= '!') {
510 /* Set the origin of the command. */
515 * Motion commands change the underlying movement (*snarl*).
516 * For example, "l" is illegal at the end of a line, but "dl"
517 * is not. Set flags so the function knows the situation.
519 F_SET(&motion
, vp
->kp
->flags
& VC_COMMASK
);
522 * Everything starts at the current position. This permits
523 * commands like 'j' and 'k', that are line oriented motions
524 * and have special cursor suck semantics when they are used
525 * as standalone commands, to ignore column positioning.
527 fm
->lno
= tm
->lno
= sp
->lno
;
528 fm
->cno
= tm
->cno
= sp
->cno
;
529 if ((motion
.kp
->func
)(sp
, ep
, &motion
, fm
, NULL
, tm
))
533 * If the underlying motion was a line motion, set the flag
534 * in the command structure. Underlying commands can also
535 * flag the movement as a line motion (see v_sentence).
537 if (F_ISSET(motion
.kp
, V_LMODE
) || F_ISSET(&motion
, VC_LMODE
))
541 * If the motion is in the reverse direction, switch the from
542 * and to MARK's so that it's always in a forward direction.
543 * Because the motion is always from the from MARK to, but not
544 * including, the to MARK, the function may have modified the
545 * from MARK, so that it gets the one-past-the-place semantics
546 * we use; see v_match() for an example. Also set a flag so
547 * that the underlying function knows that we did this; v_yank,
548 * for example, has to know so it gets the return cursor right.
550 if (tm
->lno
< fm
->lno
||
551 tm
->lno
== fm
->lno
&& tm
->cno
< fm
->cno
) {
555 F_SET(vp
, VC_REVMOVE
);
560 * If the command sets dot, save the motion structure. The
561 * motion count was changed above and needs to be reset, that's
562 * why this is done here, and not in the calling routine.
564 if (F_ISSET(vp
->kp
, V_DOT
)) {
569 /* Let the underlying function know what motion command was used. */
574 #define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c))
578 * Get the "word" the cursor is on.
581 getkeyword(sp
, ep
, kp
, flags
)
587 register size_t beg
, end
;
592 if ((p
= file_gline(sp
, ep
, sp
->lno
, &len
)) == NULL
) {
593 if (file_lline(sp
, ep
, &lno
))
598 GETLINE_ERR(sp
, sp
->lno
);
603 /* May not be a keyword at all. */
604 if (p
== NULL
|| len
== 0 ||
605 LF_ISSET(V_KEYW
) && !inword(p
[beg
]) ||
606 LF_ISSET(V_KEYNUM
) && !innum(p
[beg
]) &&
607 p
[beg
] != '-' && p
[beg
] != '+') {
608 noword
: msgq(sp
, M_BERR
, "Cursor not in a %s",
609 LF_ISSET(V_KEYW
) ? "word" : "number");
613 /* Find the beginning/end of the keyword. */
615 if (LF_ISSET(V_KEYW
)) {
618 if (!inword(p
[beg
])) {
628 if (!innum(p
[beg
])) {
629 if (beg
> 0 && p
[beg
- 1] == '0' &&
630 (p
[beg
] == 'X' || p
[beg
] == 'x'))
640 /* Skip possible leading sign. */
641 if (beg
!= 0 && p
[beg
] != '0' &&
642 (p
[beg
- 1] == '+' || p
[beg
- 1] == '-'))
646 if (LF_ISSET(V_KEYW
)) {
647 for (end
= sp
->cno
; ++end
< len
&& inword(p
[end
]););
650 for (end
= sp
->cno
; ++end
< len
;) {
651 if (p
[end
] == 'X' || p
[end
] == 'x') {
652 if (end
!= beg
+ 1 || p
[beg
] != '0')
660 /* Just a sign isn't a number. */
661 if (end
== beg
&& (p
[beg
] == '+' || p
[beg
] == '-'))
667 * Getting a keyword implies moving the cursor to its beginning.
670 if (beg
!= sp
->cno
) {
672 sp
->s_refresh(sp
, ep
);
677 * 8-bit clean problem. Numeric keywords are handled using strtol(3)
678 * and friends. This would have to be fixed in v_increment and here
679 * to not depend on a trailing NULL.
681 len
= (end
- beg
) + 2; /* XXX */
682 kp
->klen
= (end
- beg
) + 1;
683 BINC_RET(sp
, kp
->keyword
, kp
->kbuflen
, len
);
684 memmove(kp
->keyword
, p
+ beg
, kp
->klen
);
685 kp
->keyword
[kp
->klen
] = '\0'; /* XXX */
691 * Return the next count.
694 getcount(sp
, fkey
, countp
)
705 /* Assume that overflow results in a smaller number. */
706 tc
= count
* 10 + ikey
.ch
- '0';
708 /* Toss to the next non-digit. */
710 if (getkey(sp
, &ikey
,
711 TXT_MAPCOMMAND
| TXT_MAPNODIGIT
))
713 } while (isdigit(ikey
.ch
));
714 msgq(sp
, M_ERR
, "Number larger than %lu", ULONG_MAX
);
718 if (getkey(sp
, &ikey
, TXT_MAPCOMMAND
| TXT_MAPNODIGIT
))
720 } while (isdigit(ikey
.ch
));
727 * Return the next key.
730 getkey(sp
, ikeyp
, map
)
735 switch (term_key(sp
, ikeyp
, map
)) {
739 F_SET(sp
, S_EXIT_FORCE
);
744 return (ikeyp
->value
== K_ESCAPE
);