816 lines
21 KiB
C
816 lines
21 KiB
C
|
/* vi.c */
|
||
|
|
||
|
/* Author:
|
||
|
* Steve Kirkendall
|
||
|
* Beaverton, OR 97005
|
||
|
* kirkenda@cs.pdx.edu
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "config.h"
|
||
|
#include "ctype.h"
|
||
|
#include "vi.h"
|
||
|
|
||
|
|
||
|
|
||
|
/* This array describes what each key does */
|
||
|
#define NO_FUNC (MARK (*)())0
|
||
|
|
||
|
#define NO_ARGS 0
|
||
|
#define CURSOR 1
|
||
|
#define CURSOR_CNT_KEY 2
|
||
|
#define CURSOR_MOVED 3
|
||
|
#define CURSOR_EOL 4
|
||
|
#define ZERO 5
|
||
|
#define DIGIT 6
|
||
|
#define CURSOR_TEXT 7
|
||
|
#define KEYWORD 8
|
||
|
#define ARGSMASK 0x0f
|
||
|
#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)
|
||
|
#ifndef NO_SHOWMODE
|
||
|
static int keymodes[] = {0, WHEN_REP1, WHEN_CUT, WHEN_MARK, WHEN_CHAR};
|
||
|
# define KEYMODE(args) (keymodes[(args) >> 4])
|
||
|
#else
|
||
|
# define KEYMODE(args) 0
|
||
|
#endif
|
||
|
|
||
|
static struct keystru
|
||
|
{
|
||
|
MARK (*func)(); /* the function to run */
|
||
|
uchar args; /* description of the args needed */
|
||
|
#ifndef NO_VISIBLE
|
||
|
short flags;
|
||
|
#else
|
||
|
uchar flags; /* other stuff */
|
||
|
#endif
|
||
|
}
|
||
|
vikeys[] =
|
||
|
{
|
||
|
/* NUL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#ifndef NO_EXTENSIONS
|
||
|
/* ^A find cursor word */ {m_wsrch, KEYWORD, MVMT|NREL|VIZ},
|
||
|
#else
|
||
|
/* ^A not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* ^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 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},
|
||
|
#ifdef SIGTSTP
|
||
|
/* ^Z suspend elvis */ {v_suspend, NO_ARGS, NO_FLAGS},
|
||
|
#else
|
||
|
/* ^Z not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* 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, NO_FLAGS},
|
||
|
/* ^_ 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},
|
||
|
#ifndef NO_EXTENSIONS
|
||
|
/* # increment number */ {v_increment, KEYWORD, SDOT},
|
||
|
#else
|
||
|
/* # not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* $ 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},
|
||
|
#ifndef NO_SENTENCE
|
||
|
/* ( mv back sentence */ {m_sentence, CURSOR, MVMT|VIZ},
|
||
|
/* ) mv fwd sentence */ {m_sentence, CURSOR, MVMT|VIZ},
|
||
|
#else
|
||
|
/* ( not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
/* ) not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
#ifndef NO_ERRLIST
|
||
|
/* * errlist */ {v_errlist, CURSOR, FRNT|NREL},
|
||
|
#else
|
||
|
/* * not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* + mv front next ln */ {m_updnto, CURSOR, MVMT|FRNT|LNMD|VIZ|INCL},
|
||
|
#ifndef NO_CHARSEARCH
|
||
|
/* , reverse [fFtT] cmd*/ {m__ch, CURSOR, MVMT|INCL|VIZ},
|
||
|
#else
|
||
|
/* , not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* - 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},
|
||
|
#ifndef NO_CHARSEARCH
|
||
|
/* ; repeat [fFtT] cmd*/ {m__ch, CURSOR, MVMT|INCL|VIZ},
|
||
|
#else
|
||
|
/* ; not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS|VIZ},
|
||
|
#endif
|
||
|
/* < 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},
|
||
|
#ifndef NO_AT
|
||
|
/* @ execute a cutbuf */ {v_at, C_C_K_CUT, NO_FLAGS},
|
||
|
#else
|
||
|
/* @ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* 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},
|
||
|
#ifndef NO_CHARSEARCH
|
||
|
/* F move bk to char */ {m_Fch, C_C_K_CHAR, MVMT|INCL|VIZ},
|
||
|
#else
|
||
|
/* F not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* 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},
|
||
|
#ifndef NO_EXTENSIONS
|
||
|
/* K look up keyword */ {v_keyword, KEYWORD, NO_FLAGS},
|
||
|
#else
|
||
|
/* K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* 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},
|
||
|
#ifndef NO_CHARSEARCH
|
||
|
/* T move bk to char */ {m_Tch, C_C_K_CHAR, MVMT|INCL|VIZ},
|
||
|
#else
|
||
|
/* T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* U undo whole line */ {v_undoline, CURSOR, FRNT},
|
||
|
#ifndef NO_VISIBLE
|
||
|
/* V start visible */ {v_start, CURSOR, INCL|LNMD|VIZ},
|
||
|
#else
|
||
|
/* V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* W move forward Word*/ {m_fword, CURSOR, MVMT|INCL|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},
|
||
|
#ifndef NO_POPUP
|
||
|
/* \ pop-up menu */ {v_popup, CURSOR_MOVED, VIZ},
|
||
|
#else
|
||
|
/* \ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* ] 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},
|
||
|
#ifndef NO_CHARSEARCH
|
||
|
/* f move fwd for char*/ {m_fch, C_C_K_CHAR, MVMT|INCL|VIZ},
|
||
|
#else
|
||
|
/* f not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* 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},
|
||
|
#ifndef NO_CHARSEARCH
|
||
|
/* t move fwd to char */ {m_tch, C_C_K_CHAR, MVMT|INCL|VIZ},
|
||
|
#else
|
||
|
/* t not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* u undo */ {v_undo, CURSOR, NO_FLAGS},
|
||
|
#ifndef NO_VISIBLE
|
||
|
/* v start visible */ {v_start, CURSOR, INCL|VIZ},
|
||
|
#else
|
||
|
/* v not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
|
||
|
#endif
|
||
|
/* w move fwd word */ {m_fword, CURSOR, MVMT|INCL|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|LNMD|VIZ},
|
||
|
/* | move to column */ {m_tocol, CURSOR, MVMT|NREL|VIZ},
|
||
|
/* } fwd paragraph */ {m_paragraph, CURSOR, MVMT|LNMD|VIZ},
|
||
|
/* ~ upper/lowercase */ {v_ulcase, CURSOR, SDOT},
|
||
|
/* DEL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
void vi()
|
||
|
{
|
||
|
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/</>/! */
|
||
|
char text[132];
|
||
|
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 */
|
||
|
int firstkey;
|
||
|
REG int i;
|
||
|
|
||
|
/* tell the redraw() function to start from scratch */
|
||
|
redraw(MARK_UNSET, FALSE);
|
||
|
|
||
|
#ifdef lint
|
||
|
/* 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
|
||
|
* happy.
|
||
|
*/
|
||
|
range = 0L;
|
||
|
#endif
|
||
|
|
||
|
/* safeguard against '.' with no previous command */
|
||
|
dotkey = dotpkey = dotkey2 = dotcnt = 0;
|
||
|
|
||
|
/* go immediately into insert mode, if ":set inputmode" */
|
||
|
firstkey = 0;
|
||
|
#ifndef NO_EXTENSIONS
|
||
|
if (*o_inputmode)
|
||
|
{
|
||
|
firstkey = 'i';
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* 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)
|
||
|
{
|
||
|
U_line = 0L;
|
||
|
}
|
||
|
|
||
|
/* report any changes from the previous command */
|
||
|
if (rptlines >= *o_report)
|
||
|
{
|
||
|
redraw(cursor, FALSE);
|
||
|
msg("%ld line%s %s", rptlines, (rptlines==1?"":"s"), rptlabel);
|
||
|
}
|
||
|
rptlines = 0L;
|
||
|
|
||
|
/* get the next command key. It must be ASCII */
|
||
|
if (firstkey)
|
||
|
{
|
||
|
key = firstkey;
|
||
|
firstkey = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
key = getkey(WHEN_VICMD);
|
||
|
} while (key < 0 || key > 127);
|
||
|
}
|
||
|
|
||
|
/* Convert a doubled-up operator such as "dd" into "d_" */
|
||
|
if (prevkey && key == prevkey)
|
||
|
{
|
||
|
key = '_';
|
||
|
}
|
||
|
|
||
|
/* look up the structure describing this command */
|
||
|
keyptr = &vikeys[key];
|
||
|
|
||
|
/* '&' and uppercase operators always act like doubled */
|
||
|
if (!prevkey && keyptr->args == CURSOR_MOVED
|
||
|
&& (key == '&' || isupper(key)))
|
||
|
{
|
||
|
range = cursor;
|
||
|
prevkey = key;
|
||
|
key = '_';
|
||
|
keyptr = &vikeys[key];
|
||
|
}
|
||
|
|
||
|
#ifndef NO_VISIBLE
|
||
|
/* 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))
|
||
|
{
|
||
|
beep();
|
||
|
prevkey = 0;
|
||
|
count = 0;
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* if we're in the middle of a d/c/y/</>/! command, reject
|
||
|
* anything but movement.
|
||
|
*/
|
||
|
if (prevkey && !(keyptr->flags & (MVMT|PTMV)))
|
||
|
{
|
||
|
beep();
|
||
|
prevkey = 0;
|
||
|
count = 0;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* set the "dot" variables, if we're supposed to */
|
||
|
if (((keyptr->flags & SDOT)
|
||
|
|| (prevkey && vikeys[prevkey].flags & SDOT))
|
||
|
#ifndef NO_VISIBLE
|
||
|
&& !V_from
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
dotkey = key;
|
||
|
dotpkey = prevkey;
|
||
|
dotkey2 = '\0';
|
||
|
dotcnt = count;
|
||
|
|
||
|
/* 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 */
|
||
|
if (key == '.')
|
||
|
{
|
||
|
key = dotkey;
|
||
|
keyptr = &vikeys[key];
|
||
|
prevkey = dotpkey;
|
||
|
if (prevkey)
|
||
|
{
|
||
|
range = cursor;
|
||
|
}
|
||
|
if (count == 0)
|
||
|
{
|
||
|
count = dotcnt;
|
||
|
}
|
||
|
doingdot = TRUE;
|
||
|
|
||
|
/* remember the line before any changes are made */
|
||
|
if (U_line != markline(cursor))
|
||
|
{
|
||
|
U_line = markline(cursor);
|
||
|
strcpy(U_text, fetchline(U_line));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
doingdot = FALSE;
|
||
|
}
|
||
|
|
||
|
/* process the key as a command */
|
||
|
tcurs = cursor;
|
||
|
force_flags = NO_FLAGS;
|
||
|
switch (keyptr->args & ARGSMASK)
|
||
|
{
|
||
|
case ZERO:
|
||
|
if (count == 0)
|
||
|
{
|
||
|
tcurs = cursor & ~(BLKSIZE - 1);
|
||
|
break;
|
||
|
}
|
||
|
/* else fall through & treat like other digits... */
|
||
|
|
||
|
case DIGIT:
|
||
|
count = count * 10 + key - '0';
|
||
|
break;
|
||
|
|
||
|
case KEYWORD:
|
||
|
/* if not on a keyword, fail */
|
||
|
pfetch(markline(cursor));
|
||
|
key = markidx(cursor);
|
||
|
if (!isalnum(ptext[key]))
|
||
|
{
|
||
|
tcurs = MARK_UNSET;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* find the start of the keyword */
|
||
|
while (key > 0 && isalnum(ptext[key - 1]))
|
||
|
{
|
||
|
key--;
|
||
|
}
|
||
|
tcurs = (cursor & ~(BLKSIZE - 1)) + key;
|
||
|
|
||
|
/* copy it into a buffer, and NUL-terminate it */
|
||
|
i = 0;
|
||
|
do
|
||
|
{
|
||
|
text[i++] = ptext[key++];
|
||
|
} while (isalnum(ptext[key]));
|
||
|
text[i] = '\0';
|
||
|
|
||
|
/* call the function */
|
||
|
tcurs = (*keyptr->func)(text, tcurs, count);
|
||
|
count = 0L;
|
||
|
break;
|
||
|
|
||
|
case NO_ARGS:
|
||
|
if (keyptr->func)
|
||
|
{
|
||
|
(*keyptr->func)();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
beep();
|
||
|
}
|
||
|
count = 0L;
|
||
|
break;
|
||
|
|
||
|
case CURSOR:
|
||
|
tcurs = (*keyptr->func)(cursor, count, key, prevkey);
|
||
|
count = 0L;
|
||
|
break;
|
||
|
|
||
|
case CURSOR_CNT_KEY:
|
||
|
if (doingdot)
|
||
|
{
|
||
|
tcurs = (*keyptr->func)(cursor, count, dotkey2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* get a key */
|
||
|
i = getkey(KEYMODE(keyptr->args));
|
||
|
if (i == '\033') /* ESC */
|
||
|
{
|
||
|
count = 0;
|
||
|
tcurs = MARK_UNSET;
|
||
|
break; /* exit from "case CURSOR_CNT_KEY" */
|
||
|
}
|
||
|
else if (i == ctrl('V'))
|
||
|
{
|
||
|
i = getkey(0);
|
||
|
}
|
||
|
|
||
|
/* if part of an SDOT command, remember it */
|
||
|
if (keyptr->flags & SDOT
|
||
|
|| (prevkey && vikeys[prevkey].flags & SDOT))
|
||
|
{
|
||
|
dotkey2 = i;
|
||
|
}
|
||
|
|
||
|
/* do it */
|
||
|
tcurs = (*keyptr->func)(cursor, count, i);
|
||
|
}
|
||
|
count = 0L;
|
||
|
break;
|
||
|
|
||
|
case CURSOR_MOVED:
|
||
|
#ifndef NO_VISIBLE
|
||
|
if (V_from)
|
||
|
{
|
||
|
range = cursor;
|
||
|
tcurs = V_from;
|
||
|
count = 0L;
|
||
|
prevkey = key;
|
||
|
key = (V_linemd ? 'V' : 'v');
|
||
|
keyptr = &vikeys[key];
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
prevkey = key;
|
||
|
range = cursor;
|
||
|
force_flags = LNMD|INCL;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CURSOR_EOL:
|
||
|
prevkey = key;
|
||
|
/* a zero-length line needs special treatment */
|
||
|
pfetch(markline(cursor));
|
||
|
if (plen == 0)
|
||
|
{
|
||
|
/* act on a zero-length section of text */
|
||
|
range = tcurs = cursor;
|
||
|
key = '0';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* act like CURSOR_MOVED with '$' movement */
|
||
|
range = cursor;
|
||
|
tcurs = m_rear(cursor, 1L);
|
||
|
key = '$';
|
||
|
}
|
||
|
count = 0L;
|
||
|
keyptr = &vikeys[key];
|
||
|
break;
|
||
|
|
||
|
case CURSOR_TEXT:
|
||
|
do
|
||
|
{
|
||
|
text[0] = key;
|
||
|
if (vgets(key, text + 1, sizeof text - 1) >= 0)
|
||
|
{
|
||
|
/* reassure user that <CR> was hit */
|
||
|
qaddch('\r');
|
||
|
refresh();
|
||
|
|
||
|
/* call the function with the text */
|
||
|
tcurs = (*keyptr->func)(cursor, text);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (exwrote || mode == MODE_COLON)
|
||
|
{
|
||
|
redraw(MARK_UNSET, FALSE);
|
||
|
}
|
||
|
mode = MODE_VI;
|
||
|
}
|
||
|
} while (mode == MODE_COLON);
|
||
|
count = 0L;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* 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.
|
||
|
*/
|
||
|
if (mode != MODE_VI)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* now move the cursor, as appropriate */
|
||
|
if (keyptr->args == CURSOR_MOVED)
|
||
|
{
|
||
|
/* the < and > keys have FRNT,
|
||
|
* but it shouldn't be applied yet
|
||
|
*/
|
||
|
tcurs = adjmove(cursor, tcurs, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tcurs = adjmove(cursor, tcurs, (int)keyptr->flags | force_flags);
|
||
|
}
|
||
|
|
||
|
/* was that the end of a d/c/y/</>/! command? */
|
||
|
if (prevkey && ((keyptr->flags & MVMT)
|
||
|
#ifndef NO_VISIBLE
|
||
|
|| V_from
|
||
|
#endif
|
||
|
) && count == 0L)
|
||
|
{
|
||
|
#ifndef NO_VISIBLE
|
||
|
/* turn off the hilight */
|
||
|
V_from = 0L;
|
||
|
#endif
|
||
|
|
||
|
/* if the movement command failed, cancel operation */
|
||
|
if (tcurs == MARK_UNSET)
|
||
|
{
|
||
|
prevkey = 0;
|
||
|
count = 0;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* make sure range=front and tcurs=rear. Either way,
|
||
|
* leave cursor=range since that's where we started.
|
||
|
*/
|
||
|
cursor = range;
|
||
|
if (tcurs < range)
|
||
|
{
|
||
|
range = tcurs;
|
||
|
tcurs = cursor;
|
||
|
}
|
||
|
|
||
|
/* 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 & (MVMT|NREL|LNMD|FRNT|INCL)) == MVMT
|
||
|
&& markline(range) < markline(tcurs)
|
||
|
&& (markline(tcurs) > nlines || tcurs == m_front(tcurs, 0L)))
|
||
|
{
|
||
|
tcurs = (tcurs & ~(BLKSIZE - 1)) - BLKSIZE;
|
||
|
pfetch(markline(tcurs));
|
||
|
tcurs += plen;
|
||
|
}
|
||
|
|
||
|
/* adjust for line mode & inclusion of last char/line */
|
||
|
i = (keyptr->flags | vikeys[prevkey].flags);
|
||
|
switch ((i | force_flags) & (INCL|LNMD))
|
||
|
{
|
||
|
case INCL:
|
||
|
tcurs++;
|
||
|
break;
|
||
|
|
||
|
case INCL|LNMD:
|
||
|
tcurs += BLKSIZE;
|
||
|
/* fall through... */
|
||
|
|
||
|
case LNMD:
|
||
|
range &= ~(BLKSIZE - 1);
|
||
|
tcurs &= ~(BLKSIZE - 1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* run the function */
|
||
|
tcurs = (*vikeys[prevkey].func)(range, tcurs);
|
||
|
if (mode == MODE_VI)
|
||
|
{
|
||
|
(void)adjmove(cursor, cursor, 0);
|
||
|
cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags);
|
||
|
}
|
||
|
|
||
|
/* cleanup */
|
||
|
prevkey = 0;
|
||
|
}
|
||
|
else if (!prevkey)
|
||
|
{
|
||
|
if (tcurs != MARK_UNSET)
|
||
|
cursor = tcurs;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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 */
|
||
|
REG int i;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
watch();
|
||
|
#endif
|
||
|
|
||
|
/* if the command failed, bag it! */
|
||
|
if (new == MARK_UNSET)
|
||
|
{
|
||
|
beep();
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
/* if this is a non-relative movement, set the '' mark */
|
||
|
if (flags & NREL)
|
||
|
{
|
||
|
mark[26] = old;
|
||
|
}
|
||
|
|
||
|
/* make sure it isn't past the end of the file */
|
||
|
if (markline(new) < 1)
|
||
|
{
|
||
|
new = MARK_FIRST;
|
||
|
}
|
||
|
else if (markline(new) > nlines)
|
||
|
{
|
||
|
new = MARK_LAST;
|
||
|
}
|
||
|
|
||
|
/* fetch the new line */
|
||
|
pfetch(markline(new));
|
||
|
|
||
|
/* move to the front, if we're supposed to */
|
||
|
if (flags & FRNT)
|
||
|
{
|
||
|
new = m_front(new, 1L);
|
||
|
}
|
||
|
|
||
|
/* change the column#, or change the mark to suit the column# */
|
||
|
if (!(flags & NCOL))
|
||
|
{
|
||
|
/* change the column# */
|
||
|
i = markidx(new);
|
||
|
if (i == BLKSIZE - 1)
|
||
|
{
|
||
|
new &= ~(BLKSIZE - 1);
|
||
|
if (plen > 0)
|
||
|
{
|
||
|
new += plen - 1;
|
||
|
}
|
||
|
colno = BLKSIZE * 8; /* one heck of a big colno */
|
||
|
}
|
||
|
else if (plen > 0)
|
||
|
{
|
||
|
if (i >= plen)
|
||
|
{
|
||
|
new = (new & ~(BLKSIZE - 1)) + plen - 1;
|
||
|
}
|
||
|
colno = idx2col(new, ptext, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
new &= ~(BLKSIZE - 1);
|
||
|
colno = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* 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)
|
||
|
{
|
||
|
i += 2;
|
||
|
}
|
||
|
#ifndef NO_CHARATTR
|
||
|
else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
|
||
|
{
|
||
|
text += 2; /* plus one more in "for()" stmt */
|
||
|
}
|
||
|
#endif
|
||
|
else
|
||
|
{
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
if (text > ptext)
|
||
|
{
|
||
|
text--;
|
||
|
}
|
||
|
new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
|
||
|
}
|
||
|
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
watch()
|
||
|
{
|
||
|
static wasset;
|
||
|
|
||
|
if (*origname)
|
||
|
{
|
||
|
wasset = TRUE;
|
||
|
}
|
||
|
else if (wasset)
|
||
|
{
|
||
|
mode = MODE_EX;
|
||
|
msg("origname was clobbered");
|
||
|
endwin();
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
if (wasset && nlines == 0)
|
||
|
{
|
||
|
mode = MODE_EX;
|
||
|
msg("nlines=0");
|
||
|
endwin();
|
||
|
abort();
|
||
|
}
|
||
|
}
|
||
|
#endif
|