/* This array describes what each key does */
#define NO_FUNC (MARK (*)())0
#define C_C_K_REP1 (CURSOR_CNT_KEY | 0x10)
#define C_C_K_CUT (CURSOR_CNT_KEY | 0x20)
#define C_C_K_MARK (CURSOR_CNT_KEY | 0x30)
#define C_C_K_CHAR (CURSOR_CNT_KEY | 0x40)
static int keymodes
[] = {0, WHEN_REP1
, WHEN_CUT
, WHEN_MARK
, WHEN_CHAR
};
# define KEYMODE(args) (keymodes[(args) >> 4])
MARK (*func
)(); /* the function to run */
uchar args
; /* description of the args needed */
uchar flags
; /* other stuff */
/* NUL not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^A find cursor word */ {m_wsrch
, KEYWORD
, MVMT
|NREL
|VIZ
},
/* ^A not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^B page backward */ {m_scroll
, CURSOR
, FRNT
|VIZ
},
/* ^C not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^D scroll dn 1/2page*/ {m_scroll
, CURSOR
, NCOL
|VIZ
},
/* ^E scroll up */ {m_scroll
, CURSOR
, NCOL
|VIZ
},
/* ^F page forward */ {m_scroll
, CURSOR
, FRNT
|VIZ
},
/* ^G show file status */ {v_status
, NO_ARGS
, NO_FLAGS
},
/* ^H move left, like h*/ {m_left
, CURSOR
, MVMT
|VIZ
},
/* ^I not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^J move down */ {m_updnto
, CURSOR
, MVMT
|LNMD
|VIZ
|INCL
},
/* ^K not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^L redraw screen */ {v_redraw
, NO_ARGS
, NO_FLAGS
|VIZ
},
/* ^M mv front next ln */ {m_updnto
, CURSOR
, MVMT
|FRNT
|LNMD
|VIZ
|INCL
},
/* ^N move down */ {m_updnto
, CURSOR
, MVMT
|LNMD
|VIZ
|INCL
|NCOL
},
/* ^O not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^P move up */ {m_updnto
, CURSOR
, MVMT
|LNMD
|VIZ
|INCL
|NCOL
},
/* ^Q not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^R redraw screen */ {v_redraw
, NO_ARGS
, NO_FLAGS
|VIZ
},
/* ^S not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^T pop tagstack */ {v_pop
, CURSOR
, NO_FLAGS
},
/* ^T not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^U scroll up 1/2page*/ {m_scroll
, CURSOR
, NCOL
|VIZ
},
/* ^V not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^W not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^X move to phys col */ {m_tocol
, CURSOR
, MVMT
|NREL
|VIZ
},
/* ^Y scroll down */ {m_scroll
, CURSOR
, NCOL
|VIZ
},
/* ^Z suspend elvis */ {v_suspend
, NO_ARGS
, NO_FLAGS
},
/* ^Z not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ESC not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^\ not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ^] keyword is tag */ {v_tag
, KEYWORD
, NO_FLAGS
},
/* ^^ previous file */ {v_switch
, CURSOR
, FRNT
},
/* ^_ not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* SPC move right,like l*/ {m_right
, CURSOR
, MVMT
|INCL
|VIZ
},
/* ! run thru filter */ {v_filter
, CURSOR_MOVED
, FRNT
|LNMD
|INCL
|VIZ
},
/* " select cut buffer*/ {v_selcut
, C_C_K_CUT
, PTMV
|VIZ
},
/* # increment number */ {v_increment
, KEYWORD
, SDOT
},
/* # not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* $ move to rear */ {m_rear
, CURSOR
, MVMT
|INCL
|VIZ
},
/* % move to match */ {m_match
, CURSOR
, MVMT
|INCL
|VIZ
},
/* & repeat subst */ {v_again
, CURSOR_MOVED
, SDOT
|NCOL
|LNMD
|INCL
},
/* ' move to a mark */ {m_tomark
, C_C_K_MARK
, MVMT
|FRNT
|NREL
|LNMD
|INCL
|VIZ
},
/* ( mv back sentence */ {m_sentence
, CURSOR
, MVMT
|VIZ
},
/* ) mv fwd sentence */ {m_sentence
, CURSOR
, MVMT
|VIZ
},
/* ( not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ) not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* * errlist */ {v_errlist
, CURSOR
, FRNT
|NREL
},
/* * not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* + mv front next ln */ {m_updnto
, CURSOR
, MVMT
|FRNT
|LNMD
|VIZ
|INCL
},
/* , reverse [fFtT] cmd*/ {m__ch
, CURSOR
, MVMT
|INCL
|VIZ
},
/* , not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* - mv front prev ln */ {m_updnto
, CURSOR
, MVMT
|FRNT
|LNMD
|VIZ
|INCL
},
/* . special... */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* / forward search */ {m_fsrch
, CURSOR_TEXT
, MVMT
|NREL
|VIZ
},
/* 0 part of count? */ {NO_FUNC
, ZERO
, MVMT
|PTMV
|VIZ
},
/* 1 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 2 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 3 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 4 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 5 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 6 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 7 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 8 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* 9 part of count */ {NO_FUNC
, DIGIT
, PTMV
|VIZ
},
/* : run single EX cmd*/ {v_1ex
, CURSOR_TEXT
, NO_FLAGS
},
/* ; repeat [fFtT] cmd*/ {m__ch
, CURSOR
, MVMT
|INCL
|VIZ
},
/* ; not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
|VIZ
},
/* < shift text left */ {v_lshift
, CURSOR_MOVED
, SDOT
|FRNT
|LNMD
|INCL
|VIZ
},
/* = preset filter */ {v_reformat
, CURSOR_MOVED
, SDOT
|FRNT
|LNMD
|INCL
|VIZ
},
/* > shift text right */ {v_rshift
, CURSOR_MOVED
, SDOT
|FRNT
|LNMD
|INCL
|VIZ
},
/* ? backward search */ {m_bsrch
, CURSOR_TEXT
, MVMT
|NREL
|VIZ
},
/* @ execute a cutbuf */ {v_at
, C_C_K_CUT
, NO_FLAGS
},
/* @ not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* A append at EOL */ {v_insert
, CURSOR
, SDOT
},
/* B move back Word */ {m_bword
, CURSOR
, MVMT
|VIZ
},
/* C change to EOL */ {v_change
, CURSOR_EOL
, SDOT
},
/* D delete to EOL */ {v_delete
, CURSOR_EOL
, SDOT
},
/* E move end of Word */ {m_eword
, CURSOR
, MVMT
|INCL
|VIZ
},
/* F move bk to char */ {m_Fch
, C_C_K_CHAR
, MVMT
|INCL
|VIZ
},
/* F not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* G move to line # */ {m_updnto
, CURSOR
, MVMT
|NREL
|LNMD
|FRNT
|INCL
|VIZ
},
/* H move to row */ {m_row
, CURSOR
, MVMT
|LNMD
|FRNT
|VIZ
|INCL
},
/* I insert at front */ {v_insert
, CURSOR
, SDOT
},
/* J join lines */ {v_join
, CURSOR
, SDOT
},
/* K look up keyword */ {v_keyword
, KEYWORD
, NO_FLAGS
},
/* K not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* L move to last row */ {m_row
, CURSOR
, MVMT
|LNMD
|FRNT
|VIZ
|INCL
},
/* M move to mid row */ {m_row
, CURSOR
, MVMT
|LNMD
|FRNT
|VIZ
|INCL
},
/* N reverse prev srch*/ {m_nsrch
, CURSOR
, MVMT
|NREL
|VIZ
},
/* O insert above line*/ {v_insert
, CURSOR
, SDOT
},
/* P paste before */ {v_paste
, CURSOR
, SDOT
},
/* Q quit to EX mode */ {v_quit
, NO_ARGS
, NO_FLAGS
},
/* R overtype */ {v_overtype
, CURSOR
, SDOT
},
/* S change line */ {v_change
, CURSOR_MOVED
, SDOT
},
/* T move bk to char */ {m_Tch
, C_C_K_CHAR
, MVMT
|INCL
|VIZ
},
/* T not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* U undo whole line */ {v_undoline
, CURSOR
, FRNT
},
/* V start visible */ {v_start
, CURSOR
, INCL
|LNMD
|VIZ
},
/* V not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* W move forward Word*/ {m_fword
, CURSOR
, MVMT
|INCL
|NWRP
|VIZ
},
/* X delete to left */ {v_xchar
, CURSOR
, SDOT
},
/* Y yank text */ {v_yank
, CURSOR_MOVED
, NCOL
},
/* Z save file & exit */ {v_xit
, CURSOR_CNT_KEY
, NO_FLAGS
},
/* [ move back section*/ {m_paragraph
, CURSOR
, MVMT
|LNMD
|NREL
|VIZ
},
/* \ pop-up menu */ {v_popup
, CURSOR_MOVED
, VIZ
},
/* \ not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* ] move fwd section */ {m_paragraph
, CURSOR
, MVMT
|LNMD
|NREL
|VIZ
},
/* ^ move to front */ {m_front
, CURSOR
, MVMT
|VIZ
},
/* _ current line */ {m_updnto
, CURSOR
, MVMT
|LNMD
|FRNT
|INCL
},
/* ` move to mark */ {m_tomark
, C_C_K_MARK
, MVMT
|NREL
|VIZ
},
/* a append at cursor */ {v_insert
, CURSOR
, SDOT
},
/* b move back word */ {m_bword
, CURSOR
, MVMT
|VIZ
},
/* c change text */ {v_change
, CURSOR_MOVED
, SDOT
|VIZ
},
/* d delete op */ {v_delete
, CURSOR_MOVED
, SDOT
|VIZ
},
/* e move end word */ {m_eword
, CURSOR
, MVMT
|INCL
|VIZ
},
/* f move fwd for char*/ {m_fch
, C_C_K_CHAR
, MVMT
|INCL
|VIZ
},
/* f not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* g not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* h move left */ {m_left
, CURSOR
, MVMT
|VIZ
},
/* i insert at cursor */ {v_insert
, CURSOR
, SDOT
},
/* j move down */ {m_updnto
, CURSOR
, MVMT
|NCOL
|LNMD
|VIZ
|INCL
},
/* k move up */ {m_updnto
, CURSOR
, MVMT
|NCOL
|LNMD
|VIZ
|INCL
},
/* l move right */ {m_right
, CURSOR
, MVMT
|INCL
|VIZ
},
/* m define a mark */ {v_mark
, C_C_K_MARK
, NO_FLAGS
},
/* n repeat prev srch */ {m_nsrch
, CURSOR
, MVMT
|NREL
|VIZ
},
/* o insert below line*/ {v_insert
, CURSOR
, SDOT
},
/* p paste after */ {v_paste
, CURSOR
, SDOT
},
/* q not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* r replace chars */ {v_replace
, C_C_K_REP1
, SDOT
},
/* s subst N chars */ {v_subst
, CURSOR
, SDOT
},
/* t move fwd to char */ {m_tch
, C_C_K_CHAR
, MVMT
|INCL
|VIZ
},
/* t not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* u undo */ {v_undo
, CURSOR
, NO_FLAGS
},
/* v start visible */ {v_start
, CURSOR
, INCL
|VIZ
},
/* v not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
},
/* w move fwd word */ {m_fword
, CURSOR
, MVMT
|INCL
|NWRP
|VIZ
},
/* x delete character */ {v_xchar
, CURSOR
, SDOT
},
/* y yank text */ {v_yank
, CURSOR_MOVED
, NCOL
|VIZ
},
/* z adjust scrn row */ {m_z
, CURSOR_CNT_KEY
, NCOL
|VIZ
},
/* { back paragraph */ {m_paragraph
, CURSOR
, MVMT
|VIZ
},
/* | move to column */ {m_tocol
, CURSOR
, MVMT
|NREL
|VIZ
},
/* } fwd paragraph */ {m_paragraph
, CURSOR
, MVMT
|VIZ
},
/* ~ upper/lowercase */ {v_ulcase
, CURSOR
, SDOT
},
/* DEL not defined */ {NO_FUNC
, NO_ARGS
, NO_FLAGS
}
REG
int key
; /* keystroke from user */
long count
; /* numeric argument to some functions */
REG
struct keystru
*keyptr
;/* pointer to vikeys[] element */
MARK tcurs
; /* temporary cursor */
int prevkey
;/* previous key, if d/c/y/</>/! */
MARK range
; /* start of range for d/c/y/</>/! */
int dotkey
; /* last "key" of a change */
int dotpkey
;/* last "prevkey" of a change */
int dotkey2
;/* last extra "getkey()" of a change */
int dotcnt
; /* last "count" of a change */
/* tell the redraw() function to start from scratch */
redraw(MARK_UNSET
, FALSE
);
/* lint says that "range" might be used before it is set. This
* can't really happen due to the way "range" and "prevkey" are used,
* but lint doesn't know that. This line is here ONLY to keep lint
/* safeguard against '.' with no previous command */
dotkey
= dotpkey
= dotkey2
= dotcnt
= 0;
/* go immediately into insert mode, if ":set inputmode" */
/* Repeatedly handle VI commands */
for (count
= 0, prevkey
= '\0'; mode
== MODE_VI
; )
/* if we've moved off the undoable line, then we can't undo it at all */
if (markline(cursor
) != U_line
)
/* report any changes from the previous command */
if (rptlines
>= *o_report
)
msg("%ld line%s %s", rptlines
, (rptlines
==1?"":"s"), rptlabel
);
/* get the next command key. It must be ASCII */
key
= getkey(WHEN_VICMD
);
} while (key
< 0 || key
> 127);
debout("\nkey='%c'\n", key
);
/* Convert a doubled-up operator such as "dd" into "d_" */
if (prevkey
&& key
== prevkey
)
/* look up the structure describing this command */
/* '&' and uppercase operators always act like doubled */
if (!prevkey
&& keyptr
->args
== CURSOR_MOVED
&& (key
== '&' || isupper(key
)))
/* if we're in the middle of a v/V command, reject commands
* that aren't operators or movement commands
if (V_from
&& !(keyptr
->flags
& VIZ
))
/* if we're in the middle of a d/c/y/</>/! command, reject
if (prevkey
&& !(keyptr
->flags
& (MVMT
|PTMV
)))
/* set the "dot" variables, if we're supposed to */
if (((keyptr
->flags
& SDOT
)
|| (prevkey
&& vikeys
[prevkey
].flags
& SDOT
))
/* remember the line before any changes are made */
if (U_line
!= markline(cursor
))
U_line
= markline(cursor
);
strcpy(U_text
, fetchline(U_line
));
/* if this is "." then set other vars from the "dot" vars */
/* remember the line before any changes are made */
if (U_line
!= markline(cursor
))
U_line
= markline(cursor
);
strcpy(U_text
, fetchline(U_line
));
/* process the key as a command */
switch (keyptr
->args
& ARGSMASK
)
tcurs
= cursor
& ~(BLKSIZE
- 1);
/* else fall through & treat like other digits... */
count
= count
* 10 + key
- '0';
/* if not on a keyword, fail */
pfetch(markline(cursor
));
if (!isalnum(ptext
[key
]))
/* find the start of the keyword */
while (key
> 0 && isalnum(ptext
[key
- 1]))
tcurs
= (cursor
& ~(BLKSIZE
- 1)) + key
;
/* copy it into a buffer, and NUL-terminate it */
text
[i
++] = ptext
[key
++];
} while (isalnum(ptext
[key
]));
tcurs
= (*keyptr
->func
)(text
, tcurs
, count
);
tcurs
= (*keyptr
->func
)(cursor
, count
, key
, prevkey
);
tcurs
= (*keyptr
->func
)(cursor
, count
, dotkey2
);
i
= getkey(KEYMODE(keyptr
->args
));
if (i
== '\033') /* ESC */
break; /* exit from "case CURSOR_CNT_KEY" */
/* if part of an SDOT command, remember it */
|| (prevkey
&& vikeys
[prevkey
].flags
& SDOT
))
tcurs
= (*keyptr
->func
)(cursor
, count
, i
);
key
= (V_linemd
? 'V' : 'v');
/* a zero-length line needs special treatment */
pfetch(markline(cursor
));
/* act on a zero-length section of text */
/* act like CURSOR_MOVED with '$' movement */
tcurs
= m_rear(cursor
, 1L);
if (doingdot
|| vgets(key
, text
+ 1, sizeof text
- 1) >= 0)
/* reassure user that <CR> was hit */
/* call the function with the text */
tcurs
= (*keyptr
->func
)(cursor
, text
);
if (exwrote
|| mode
== MODE_COLON
)
redraw(MARK_UNSET
, FALSE
);
} while (mode
== MODE_COLON
);
/* if that command took us out of vi mode, then exit the loop
* NOW, without tweaking the cursor or anything. This is very
* important when mode == MODE_QUIT.
/* now move the cursor, as appropriate */
if (prevkey
&& ((keyptr
->flags
& MVMT
)
/* movements used as targets are less strict */
tcurs
= adjmove(cursor
, tcurs
, (int)(keyptr
->flags
| force_flags
));
else if (keyptr
->args
== CURSOR_MOVED
)
/* the < and > keys have FRNT,
* but it shouldn't be applied yet
tcurs
= adjmove(cursor
, tcurs
, FINL
);
tcurs
= adjmove(cursor
, tcurs
, (int)(keyptr
->flags
| force_flags
| FINL
));
/* was that the end of a d/c/y/</>/! command? */
if (prevkey
&& ((keyptr
->flags
& MVMT
)
/* turn off the hilight */
/* if the movement command failed, cancel operation */
/* make sure range=front and tcurs=rear. Either way,
* leave cursor=range since that's where we started.
/* The 'w' and 'W' destinations should never take us
* to the front of a line. Instead, they should take
* us only to the end of the preceding line.
if ((keyptr
->flags
& NWRP
) == NWRP
&& markline(range
) < markline(tcurs
)
&& (markline(tcurs
) > nlines
|| tcurs
== m_front(tcurs
, 0L)))
tcurs
= (tcurs
& ~(BLKSIZE
- 1)) - BLKSIZE
;
/* adjust for line mode & inclusion of last char/line */
i
= (keyptr
->flags
| vikeys
[prevkey
].flags
);
switch ((i
| force_flags
) & (INCL
|LNMD
))
tcurs
= (*vikeys
[prevkey
].func
)(range
, tcurs
);
(void)adjmove(cursor
, cursor
, FINL
);
cursor
= adjmove(cursor
, tcurs
, (int)(vikeys
[prevkey
].flags
| FINL
));
/* This function adjusts the MARK value that they return; here we make sure
* it isn't past the end of the line, and that the column hasn't been
* *accidentally* changed.
MARK
adjmove(old
, new, flags
)
MARK old
; /* the cursor position before the command */
REG MARK
new; /* the cursor position after the command */
int flags
; /* various flags regarding cursor mvmt */
static int colno
; /* the column number that we want */
REG
char *text
; /* used to scan through the line's text */
debout("adjmove(%ld.%d, %ld.%d, 0x%x)\n", markline(old
), markidx(old
), markline(new), markidx(new), flags
);
/* if the command failed, bag it! */
/* if this is a non-relative movement, set the '' mark */
/* make sure it isn't past the end of the file */
else if (markline(new) > nlines
)
/* move to the front, if we're supposed to */
/* change the column#, or change the mark to suit the column# */
colno
= BLKSIZE
* 8; /* one heck of a big colno */
new = (new & ~(BLKSIZE
- 1)) + plen
- 1;
colno
= idx2col(new, ptext
, FALSE
);
/* adjust the mark to get as close as possible to column# */
for (i
= 0, text
= ptext
; i
<= colno
&& *text
; text
++)
if (*text
== '\t' && !*o_list
)
i
+= *o_tabstop
- (i
% *o_tabstop
);
else if (UCHAR(*text
) < ' ' || *text
== 127)
else if (*o_charattr
&& text
[0] == '\\' && text
[1] == 'f' && text
[2])
text
+= 2; /* plus one more in "for()" stmt */
new = (new & ~(BLKSIZE
- 1)) + (int)(text
- ptext
);
msg("origname was clobbered");
if (wasset
&& nlines
== 0)