* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)vi.c 8.61 (Berkeley) 4/10/94";
static int getcmd
__P((SCR
*, EXF
*,
VICMDARG
*, VICMDARG
*, VICMDARG
*, int *));
getcount
__P((SCR
*, ARG_CHAR_T
, u_long
*));
getkey
__P((SCR
*, CH
*, u_int
));
static int getkeyword
__P((SCR
*, EXF
*, VICMDARG
*, u_int
));
static int getmotion
__P((SCR
*, EXF
*, VICMDARG
*, VICMDARG
*));
* The dot structure can be set by the underlying vi functions,
* see v_Put() and v_put().
#define DOT (&VIP(sp)->sdot)
#define DOTMOTION (&VIP(sp)->sdotmotion)
/* Start vi and paint the screen. */
if (sp
->s_refresh(sp
, ep
)) {
/* Command initialization. */
memset(&cmd
, 0, sizeof(VICMDARG
));
for (eval
= 0, vp
= &cmd
;;) {
if (!MAPPED_KEYS_WAITING(sp
) && log_cursor(sp
, ep
))
* We get a command, which may or may not have an associated
* motion. If it does, we get it too, calling its underlying
* function to get the resulting mark. We then call the
* command setting the cursor to the resulting mark.
if (getcmd(sp
, ep
, DOT
, vp
, NULL
, &comcount
))
* Historical practice: if a dot command gets a new count,
* any motion component goes away, i.e. "d3w2." deletes a
if (F_ISSET(vp
, VC_ISDOT
) && comcount
)
/* Copy the key flags into the local structure. */
F_SET(vp
, vp
->kp
->flags
);
/* Get any associated keyword. */
if (F_ISSET(vp
, V_KEYNUM
| V_KEYW
) &&
getkeyword(sp
, ep
, vp
, vp
->flags
))
/* If a non-relative movement, copy the future absolute mark. */
if (F_ISSET(vp
, V_ABS
)) {
* Set the three cursor locations to the current cursor. The
* underlying routines don't bother if the cursor doesn't move.
* This also handles line commands (e.g. Y) defaulting to the
vp
->m_start
.lno
= vp
->m_stop
.lno
= vp
->m_final
.lno
= sp
->lno
;
vp
->m_start
.cno
= vp
->m_stop
.cno
= vp
->m_final
.cno
= sp
->cno
;
* Do any required motion; getmotion sets the from MARK and the
* line mode flag. We save off the RCM mask and only restore
* it if it no RCM flags are set by the motion command. This
* means that the motion command is expected to determine where
if (F_ISSET(vp
, V_MOTION
)) {
flags
= F_ISSET(vp
, VM_RCM_MASK
);
if (getmotion(sp
, ep
, DOTMOTION
, vp
))
if (F_ISSET(vp
, VM_NOMOTION
))
if (!F_ISSET(vp
, VM_RCM_MASK
))
* If a count is set and the command is line oriented, set the
* to MARK here relative to the cursor/from MARK. This is for
* commands that take both counts and motions, i.e. "4yy" and
* "y%". As there's no way the command can know which the user
* did, we have to do it here. (There are commands that are
* line oriented and that take counts ("#G", "#H"), for which
* this calculation is either completely meaningless or wrong.
* Each command must validate the value for itself.
if (F_ISSET(vp
, VC_C1SET
) && F_ISSET(vp
, VM_LMODE
))
vp
->m_stop
.lno
+= vp
->count
- 1;
/* Increment the command count. */
/* Clear interrupt bits, save the mode and call the function. */
F_CLR(sp
, S_INTERRUPTED
| S_INTERRUPTIBLE
);
saved_mode
= F_ISSET(sp
, S_SCREENS
| S_MAJOR_CHANGE
);
if ((vp
->kp
->func
)(sp
, ep
, vp
))
/* Make sure no function left the temporary space locked. */
if (F_ISSET(sp
->gp
, G_TMP_INUSE
)) {
"Error: vi: temporary buffer not released.");
* If that command took us out of vi or changed the screen,
* then exit the loop without further action.
if (saved_mode
!= F_ISSET(sp
, S_SCREENS
| S_MAJOR_CHANGE
))
/* Set the absolute mark. */
if (F_ISSET(vp
, V_ABS
) && mark_set(sp
, ep
, ABSMARK1
, &abs
, 1))
/* Set the dot command structure. */
if (F_ISSET(vp
, V_DOT
)) {
* If a count was supplied for both the command and
* its motion, the count was used only for the motion.
* Turn the count back on for the dot structure.
if (F_ISSET(vp
, VC_C1RESET
))
* Some vi row movements are "attracted" to the last position
* set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
* commands' candle. It's broken into two parts. Here we deal
* with the command flags. In sp->relative(), we deal with the
* screen flags. If the movement is to the EOL the vi command
* handles it. If it's to the beginning, we handle it here.
* Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
* flag, but do the work themselves. The reason is that they
* have to modify the column in case they're being used as a
* motion component. Other similar commands (e.g. +, -) don't
* have to modify the column because they are always line mode
* operations when used as motions, so the column number isn't
* Does this totally violate the screen and editor layering?
* You betcha. As they say, if you think you understand it,
switch (F_ISSET(vp
, VM_RCM_MASK
)) {
vp
->m_final
.cno
= sp
->s_rcm(sp
, ep
, vp
->m_final
.lno
);
* If we changed lines, move to the first non-blank.
* This is the hack that makes logical scrolling on
* really long lines work.
if (vp
->m_start
.lno
!= vp
->m_final
.lno
) {
vp
->m_final
.lno
, &vp
->m_final
.cno
))
if (nonblank(sp
, ep
, vp
->m_final
.lno
, &vp
->m_final
.cno
))
sp
->lno
= vp
->m_final
.lno
;
sp
->cno
= vp
->m_final
.cno
;
if (!MAPPED_KEYS_WAITING(sp
)) {
err
: term_map_flush(sp
, "Vi error");
/* Refresh the screen. */
if (sp
->s_refresh(sp
, ep
)) {
/* Set the new favorite position. */
if (F_ISSET(vp
, VM_RCM_SET
)) {
(void)sp
->s_column(sp
, ep
, &sp
->rcm
);
return (v_end(sp
) || eval
);
#define KEY(key, map) { \
if (getkey(sp, &ikey, map)) \
* The command structure for vi is less complex than ex (and don't think
* I'm not grateful!) The command syntax is:
* [count] [buffer] [count] key [[motion] | [buffer] [character]]
* and there are several special cases. The motion value is itself a vi
* command, with the syntax:
* [count] key [character]
getcmd(sp
, ep
, dp
, vp
, ismotion
, comcountp
)
VICMDARG
*ismotion
; /* Previous key if getting motion component. */
/* Refresh the command structure. */
memset(&vp
->vp_startzero
, 0,
(char *)&vp
->vp_endzero
- (char *)&vp
->vp_startzero
);
/* An escape bells the user if in command mode. */
if (getkey(sp
, &ikey
, TXT_MAPCOMMAND
)) {
if (ikey
.value
== K_ESCAPE
&& ismotion
== NULL
)
msgq(sp
, M_BERR
, "Already in command mode");
/* Pick up optional buffer. */
KEY(key
, TXT_MAPCOMMAND
);
* Pick up optional count, where a leading 0 is not a count,
if (isdigit(key
) && key
!= '0') {
if (getcount(sp
, key
, &vp
->count
))
KEY(key
, TXT_MAPCOMMAND
);
/* Pick up optional buffer. */
if (F_ISSET(vp
, VC_BUFFER
)) {
msgq(sp
, M_ERR
, "Only one buffer can be specified.");
KEY(key
, TXT_MAPCOMMAND
);
/* Check for an OOB command key. */
msgq(sp
, M_BERR
, "%s isn't a vi command", KEY_NAME(sp
, key
));
* Find the command. The only legal command with no underlying
kp
= vp
->kp
= &vikeys
[vp
->key
= key
];
"%s isn't a command", KEY_NAME(sp
, key
));
/* If called for a motion command, stop now. */
/* A repeatable command must have been executed. */
if (!F_ISSET(dp
, VC_ISDOT
)) {
msgq(sp
, M_ERR
, "No command to repeat.");
* If a '.' is immediately entered after an undo command, we
* replay the log instead of redoing the last command. This
* is necessary because 'u' can't set the dot command -- see
* vi/v_undo.c:v_undo for details.
if (VIP(sp
)->u_ccnt
== sp
->ccnt
) {
/* Set new count/buffer, if any, and return. */
if (F_ISSET(vp
, VC_C1SET
)) {
if (F_ISSET(vp
, VC_BUFFER
))
/* Check for illegal count. */
if (F_ISSET(vp
, VC_C1SET
) && !LF_ISSET(V_CNT
))
/* Illegal motion command. */
if (!LF_ISSET(V_OBUF
) && F_ISSET(vp
, VC_BUFFER
))
* Special case: '[', ']' and 'Z' commands. Doesn't the fact that
* the *single* characters don't mean anything but the *doubled*
* characters do just frost your shorts?
if (vp
->key
== '[' || vp
->key
== ']' || vp
->key
== 'Z') {
KEY(key
, TXT_MAPCOMMAND
);
usage
: msgq(sp
, M_ERR
, "Usage: %s", ismotion
!= NULL
?
vikeys
[ismotion
->key
].usage
: kp
->usage
);
/* Special case: 'z' command. */
if (isdigit(vp
->character
)) {
if (getcount(sp
, vp
->character
, &vp
->count2
))
* Commands that have motion components can be doubled to
* imply the current line.
if (ismotion
!= NULL
&& ismotion
->key
!= key
&& !LF_ISSET(V_MOVE
)) {
msgq(sp
, M_ERR
, "%s may not be used as a motion command.",
/* Required character. */
* Get resulting motion mark.
getmotion(sp
, ep
, dm
, vp
)
/* If '.' command, use the dot motion, else get the motion command. */
if (F_ISSET(vp
, VC_ISDOT
)) {
F_SET(&motion
, VC_ISDOT
);
} else if (getcmd(sp
, ep
, NULL
, &motion
, vp
, ¬used
))
* A count may be provided both to the command and to the motion, in
* which case the count is multiplicative. For example, "3y4y" is the
* same as "12yy". This count is provided to the motion command and
* not to the regular function.
cnt
= motion
.count
= F_ISSET(&motion
, VC_C1SET
) ? motion
.count
: 1;
if (F_ISSET(vp
, VC_C1SET
)) {
motion
.count
*= vp
->count
;
F_SET(&motion
, VC_C1SET
);
* Set flags to restore the original values of the command
* structure so dot commands can change the count values,
* e.g. "2dw" "3." deletes a total of five words.
* Some commands can be repeated to indicate the current line. In
* this case, or if the command is a "line command", set the flags
* appropriately. If not a doubled command, run the function to get
if (vp
->key
== motion
.key
) {
F_SET(vp
, VM_LDOUBLE
| VM_LMODE
);
/* Set the origin of the command. */
vp
->m_start
.lno
= sp
->lno
;
* Set the end of the command.
* If the current line is missing, i.e. the file is empty,
* historic vi permitted a "cc" or "!!" command to insert
vp
->m_stop
.lno
= sp
->lno
+ motion
.count
- 1;
if (file_gline(sp
, ep
, vp
->m_stop
.lno
, &len
) == NULL
) {
if (vp
->m_stop
.lno
!= 1 ||
vp
->key
!= 'c' && vp
->key
!= '!') {
vp
->m_stop
.cno
= len
? len
- 1 : 0;
* Motion commands change the underlying movement (*snarl*).
* For example, "l" is illegal at the end of a line, but "dl"
* is not. Set flags so the function knows the situation.
F_SET(&motion
, vp
->kp
->flags
& VC_COMMASK
);
/* Copy the key flags into the local structure. */
F_SET(&motion
, motion
.kp
->flags
);
* Set the three cursor locations to the current cursor. This
* permits commands like 'j' and 'k', that are line oriented
* motions and have special cursor suck semantics when they are
* used as standalone commands, to ignore column positioning.
motion
.m_stop
.lno
= motion
.m_start
.lno
= sp
->lno
;
motion
.m_stop
.cno
= motion
.m_start
.cno
= sp
->cno
;
if ((motion
.kp
->func
)(sp
, ep
, &motion
))
* Copy line mode and cursor position information from the
* motion command structure. The commands can flag the
* movement as a line motion (see v_sentence) as well as set
* the VM_RCM_* flags explicitly.
F_ISSET(&motion
, VM_LMODE
| VM_NOMOTION
| VM_RCM_MASK
));
* Motion commands can reset all of the cursor information.
* If the motion is in the reverse direction, switch the
* from and to MARK's so that it's in a forward direction.
* Motions are from the from MARK to the to MARK (inclusive).
if (motion
.m_start
.lno
> motion
.m_stop
.lno
||
motion
.m_start
.lno
== motion
.m_stop
.lno
&&
motion
.m_start
.cno
> motion
.m_stop
.cno
) {
vp
->m_start
= motion
.m_stop
;
vp
->m_stop
= motion
.m_start
;
vp
->m_start
= motion
.m_start
;
vp
->m_stop
= motion
.m_stop
;
vp
->m_final
= motion
.m_final
;
* If the command sets dot, save the motion structure. The motion
* count was changed above and needs to be reset, that's why this
* is done here, and not in the calling routine.
if (F_ISSET(vp
->kp
, V_DOT
)) {
#define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c))
* Get the "word" the cursor is on.
getkeyword(sp
, ep
, kp
, flags
)
if ((p
= file_gline(sp
, ep
, sp
->lno
, &len
)) == NULL
) {
if (file_lline(sp
, ep
, &lno
))
GETLINE_ERR(sp
, sp
->lno
);
/* May not be a keyword at all. */
if (p
== NULL
|| len
== 0 ||
LF_ISSET(V_KEYW
) && !inword(p
[beg
]) ||
LF_ISSET(V_KEYNUM
) && !innum(p
[beg
]) &&
p
[beg
] != '-' && p
[beg
] != '+') {
noword
: msgq(sp
, M_BERR
, "Cursor not in a %s",
LF_ISSET(V_KEYW
) ? "word" : "number");
* Find the beginning/end of the keyword. Keywords (V_KEYW) are
* used for cursor-word searching and for tags. Historical vi
* only used the word in a tag search from the cursor to the end
* of the word, i.e. if the cursor was on the 'b' in " abc ", the
* tag was "bc". For no particular reason, we make cursor word
* searches follow the same rule.
#ifdef MOVE_TO_KEYWORD_BEGINNING
if (beg
> 0 && p
[beg
- 1] == '0' &&
(p
[beg
] == 'X' || p
[beg
] == 'x'))
/* Skip possible leading sign. */
if (beg
!= 0 && p
[beg
] != '0' &&
(p
[beg
- 1] == '+' || p
[beg
- 1] == '-'))
for (end
= sp
->cno
; ++end
< len
&& inword(p
[end
]););
for (end
= sp
->cno
; ++end
< len
;) {
if (p
[end
] == 'X' || p
[end
] == 'x') {
if (end
!= beg
+ 1 || p
[beg
] != '0')
/* Just a sign isn't a number. */
if (end
== beg
&& (p
[beg
] == '+' || p
[beg
] == '-'))
* Getting a keyword implies moving the cursor to its beginning.
* 8-bit clean problem. Numeric keywords are handled using strtol(3)
* and friends. This would have to be fixed in v_increment and here
* to not depend on a trailing NULL.
len
= (end
- beg
) + 2; /* XXX */
kp
->klen
= (end
- beg
) + 1;
BINC_RET(sp
, kp
->keyword
, kp
->kbuflen
, len
);
memmove(kp
->keyword
, p
+ beg
, kp
->klen
);
kp
->keyword
[kp
->klen
] = '\0'; /* XXX */
getcount(sp
, fkey
, countp
)
/* Assume that overflow results in a smaller number. */
tc
= count
* 10 + ikey
.ch
- '0';
/* Toss to the next non-digit. */
TXT_MAPCOMMAND
| TXT_MAPNODIGIT
))
} while (isdigit(ikey
.ch
));
msgq(sp
, M_ERR
, "Number larger than %lu", ULONG_MAX
);
if (getkey(sp
, &ikey
, TXT_MAPCOMMAND
| TXT_MAPNODIGIT
))
} while (isdigit(ikey
.ch
));
switch (term_key(sp
, ikeyp
, map
)) {
return (ikeyp
->value
== K_ESCAPE
);