home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
CMDS
/
pvic_10a.lzh
/
SRCE
/
normal.c
< prev
next >
Wrap
Text File
|
1998-04-23
|
19KB
|
983 lines
/*
* Contains the main routine for processing characters in command mode.
* Communicates closely with the code in ops.c to handle the operators.
*/
#include <stdio.h>
#include "pvic.h"
#include "locdefs.h"
/*
* Generally speaking, every command in normal() should either clear any
* pending operator (with CLEAROP), or set the motion type variable.
*/
#define CLEAROP (operator=NOP) /* clear any pending operator */
int operator = NOP; /* current pending operator */
int mtype; /* type of the current cursor motion */
int mincl; /* true if char motion is inclusive */
LPTR startop; /* cursor pos. at start of operator */
/*
* Operators can have counts either before the operator, or between the
* operator and the following cursor motion as in:
*
* d3w or 3dw
*
* If a count is given before the operator, it is saved in opnum. If
* normal() is called with a pending operator, the count in opnum (if
* present) overrides any count that came later.
*/
static int opnum = 0;
#define DEFAULT1(x) (((x) == 0) ? 1 : (x))
/*
* normal(c)
*
* Execute a command in command mode.
*
* This is basically a big switch with the cases arranged in rough categories
* in the following order:
*
* 1. File positioning commands
* 2. Control commands (e.g. ^G, Z, screen redraw, etc)
* 3. Character motions
* 4. Search commands (of various kinds)
* 5. Edit commands (e.g. J, x, X)
* 6. Insert commands (e.g. i, o, O, A)
* 7. Operators
* 8. Abbreviations (e.g. D, C)
* 9. Marks
*/
void normal(c)
int c;
{
register int n;
register char *s; /* temporary variable for misc. strings */
int flag = (0);
int type = 0; /* used in some operations to modify type */
int dir = SEARCH_FORWARD; /* search direction */
int nchar = '\0';
int finish_op;
/*
* If there is an operator pending, then the command we take
* this time will terminate it. Finish_op tells us to finish
* the operation before returning this time (unless the operation
* was cancelled.
*/
finish_op = (operator != NOP);
/*
* If we're in the middle of an operator AND we had a count before
* the operator, then that count overrides the current value of
* prenum. What this means effectively, is that commands like
* "3dw" get turned into "d3w" which makes things fall into place
* pretty neatly.
*/
if (finish_op) {
if (opnum != 0)
prenum = opnum;
} else
opnum = 0;
u_l_check(); /* clear the "line undo" buffer if we've moved */
switch (c & 0xff) {
/*
* Screen positioning commands
*/
case CTRL_D:
CLEAROP;
if (prenum)
PARAMETER_VALUE(PARAMETER_SCROLL) = (prenum > current_lines-1) ? current_lines-1 : prenum;
scroll_up(PARAMETER_VALUE(PARAMETER_SCROLL));
one_down(PARAMETER_VALUE(PARAMETER_SCROLL));
update_screen(0);
break;
case CTRL_U:
CLEAROP;
if (prenum)
PARAMETER_VALUE(PARAMETER_SCROLL) = (prenum > current_lines-1) ? current_lines-1 : prenum;
scroll_down(PARAMETER_VALUE(PARAMETER_SCROLL));
one_up(PARAMETER_VALUE(PARAMETER_SCROLL));
update_screen(0);
break;
/*
* This is kind of a hack. If we're moving by one page, the calls
* to put_string_into_input_buffer() do exactly the right thing in terms of leaving
* some context, and so on. If a count was given, we don't have
* to worry about these issues.
*/
case CTRL_F:
CLEAROP;
n = DEFAULT1(prenum);
if (n > 1) {
if ( ! one_down(current_lines * n) )
beep();
update_cursor(0);
} else {
/* clear_screen(); */
put_string_into_input_buffer("Lz\nM");
}
break;
case CTRL_B:
CLEAROP;
n = DEFAULT1(prenum);
if (n > 1) {
if ( ! one_up(current_lines * n) )
beep();
update_cursor(0);
} else {
/* clear_screen(); */
put_string_into_input_buffer("Hz-M");
}
break;
case CTRL_E:
CLEAROP;
scroll_up(DEFAULT1(prenum));
update_screen(0);
break;
case CTRL_Y:
CLEAROP;
scroll_down(DEFAULT1(prenum));
update_screen(0);
break;
case 'z':
CLEAROP;
switch (get_char_from_input_buffer()) {
case CTRL_J: /* put cursor_char at top of screen */
case CTRL_M:
*top_char = *cursor_char;
top_char->index = 0;
update_screen(0);
break;
case '.': /* put cursor_char in middle of screen */
n = current_lines/2;
goto dozcmd;
case '-': /* put cursor_char at bottom of screen */
n = current_lines-1;
/* fall through */
dozcmd:
{
register LPTR *lp = cursor_char;
register int l = 0;
while ((l < n) && (lp != NULL)) {
l += plines(lp);
*top_char = *lp;
lp = previous_line(lp);
}
}
top_char->index = 0;
update_screen(0);
break;
default:
beep();
}
break;
/*
* Control commands
*/
case ':':
CLEAROP;
if ((s = getcmdln(c)) != NULL)
do_command_line(s);
break;
case CTRL_L:
CLEAROP;
clear_screen();
update_screen(0);
break;
case CTRL_O: /* ignored */
/*
* A command that's ignored can be useful. We use it at
* times when we want to postpone redraws. By put_string_into_input_bufferg
* in a control-o, redraws get suspended until the editor
* gets back around to processing input.
*/
break;
case CTRL_G:
CLEAROP;
file_info();
break;
case CTRL_CIRCUMFLEX: /* shorthand command */
CLEAROP;
put_string_into_input_buffer(":e #\n");
break;
case 'Z': /* write, if changed, and exit */
if (get_char_from_input_buffer() != 'Z') {
beep();
break;
}
do_exit();
break;
/*
* Macro evaluates true if char 'c' is a valid identifier character
*/
# define IDCHAR(c) (is_alpha(c) || is_digit(c) || (c) == '_')
case CTRL_RIGHT_BRACKET: /* :ta to current identifier */
CLEAROP;
{
char ch;
LPTR save;
save = *cursor_char;
/*
* First back up to start of identifier. This
* doesn't match the real vi but I like it a
* little better and it shouldn't bother anyone.
*/
ch = gchar(cursor_char);
while (IDCHAR(ch)) {
if (!one_left())
break;
ch = gchar(cursor_char);
}
if (!IDCHAR(ch))
one_right();
put_string_into_input_buffer(":ta ");
/*
* Now grab the chars in the identifier
*/
ch = gchar(cursor_char);
while (IDCHAR(ch)) {
put_string_into_input_buffer(mkstr(ch));
if (!one_right())
break;
ch = gchar(cursor_char);
}
put_string_into_input_buffer("\n");
*cursor_char = save; /* restore, in case of error */
}
break;
/*
* Character motion commands
*/
case 'G':
mtype = MLINE;
*cursor_char = *goto_line(prenum);
begin_line((1));
break;
case 'H':
mtype = MLINE;
*cursor_char = *top_char;
for (n = prenum; n && one_down(1) ;n--)
;
begin_line((1));
break;
case 'M':
mtype = MLINE;
*cursor_char = *top_char;
for (n = 0; n < current_lines/2 && one_down(1) ;n++)
;
begin_line((1));
break;
case 'L':
mtype = MLINE;
*cursor_char = *previous_line(bottom_char);
for (n = prenum; n && one_up(1) ;n--)
;
begin_line((1));
break;
case 'l':
case ' ':
mtype = MCHAR;
mincl = (0);
n = DEFAULT1(prenum);
while (n--) {
if ( ! one_right() )
beep();
}
set_wanted_cursor_column = (1);
break;
case 'h':
case CTRL_H:
mtype = MCHAR;
mincl = (0);
n = DEFAULT1(prenum);
while (n--) {
if ( ! one_left() )
beep();
}
set_wanted_cursor_column = (1);
break;
case '-':
flag = (1);
/* fall through */
case 'k':
case CTRL_P:
mtype = MLINE;
if ( ! one_up(DEFAULT1(prenum)) )
beep();
if (flag)
begin_line((1));
break;
case '+':
case CTRL_J:
case CTRL_M:
flag = (1);
/* fall through */
case 'j':
case CTRL_N:
mtype = MLINE;
if ( ! one_down(DEFAULT1(prenum)) )
beep();
if (flag)
begin_line((1));
break;
/*
* This is a strange motion command that helps make operators
* more logical. It is actually implemented, but not documented
* in the real 'vi'. This motion command actually refers to "the
* current line". Commands like "dd" and "yy" are really an alternate
* form of "d_" and "y_". It does accept a count, so "d3_" works to
* delete 3 lines.
*/
case '_':
lineop:
mtype = MLINE;
one_down(DEFAULT1(prenum)-1);
break;
case '|':
mtype = MCHAR;
mincl = (1);
begin_line((0));
if (prenum > 0)
*cursor_char = *advance_column(cursor_char, prenum-1);
wanted_cursor_column = prenum - 1;
break;
/*
* Word Motions
*/
case 'B':
type = 1;
/* fall through */
case 'b':
mtype = MCHAR;
mincl = (0);
set_wanted_cursor_column = (1);
for (n = DEFAULT1(prenum); n > 0 ;n--) {
LPTR *pos;
if ((pos = back_word(cursor_char, type)) == NULL) {
beep();
CLEAROP;
break;
} else
*cursor_char = *pos;
}
break;
case 'W':
type = 1;
/* fall through */
case 'w':
/*
* This is a little strange. To match what the real vi
* does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
* This seems impolite at first, but it's really more
* what we mean when we say 'cw'.
*/
if (operator == CHANGE)
goto do_e_command;
mtype = MCHAR;
mincl = (0);
set_wanted_cursor_column = (1);
for (n = DEFAULT1(prenum); n > 0 ;n--) {
LPTR *pos;
if ((pos = forward_word(cursor_char, type)) == NULL) {
beep();
CLEAROP;
break;
} else
*cursor_char = *pos;
}
break;
case 'E':
type = 1;
/* fall through */
case 'e':
do_e_command:
mtype = MCHAR;
mincl = (1);
set_wanted_cursor_column = (1);
for (n = DEFAULT1(prenum); n > 0 ;n--) {
LPTR *pos;
/*
* The first motion gets special treatment if we're
* do a 'CHANGE'.
*/
if (n == DEFAULT1(prenum))
pos = end_word(cursor_char,type,operator==CHANGE);
else
pos = end_word(cursor_char, type, (0));
if (pos == NULL) {
beep();
CLEAROP;
break;
} else
*cursor_char = *pos;
}
break;
case '$':
mtype = MCHAR;
mincl = (1);
while ( one_right() )
;
wanted_cursor_column = 999; /* so we stay at the end */
break;
case '^':
mtype = MCHAR;
mincl = (0);
begin_line((1));
break;
case '0':
mtype = MCHAR;
mincl = (1);
begin_line((0));
break;
/*
* Searches of various kinds
*/
case '?':
case '/':
s = getcmdln(c); /* get the search string */
/*
* If they backspaced out of the search command,
* just bag everything.
*/
if (s == NULL) {
CLEAROP;
break;
}
mtype = MCHAR;
mincl = (0);
set_wanted_cursor_column = (1);
/*
* If no string given, pass NULL to repeat the prior search.
* If the search fails, abort any pending operator.
*/
if (!do_search(
(c == '/') ? SEARCH_FORWARD : SEARCH_BACKWARD,
(*s == '\0') ? NULL : s
))
CLEAROP;
break;
case 'n':
mtype = MCHAR;
mincl = (0);
set_wanted_cursor_column = (1);
if (!rep_search(0))
CLEAROP;
break;
case 'N':
mtype = MCHAR;
mincl = (0);
set_wanted_cursor_column = (1);
if (!rep_search(1))
CLEAROP;
break;
/*
* Character searches
*/
case 'T':
dir = SEARCH_BACKWARD;
/* fall through */
case 't':
type = 1;
goto docsearch;
case 'F':
dir = SEARCH_BACKWARD;
/* fall through */
case 'f':
docsearch:
mtype = MCHAR;
mincl = (1);
set_wanted_cursor_column = (1);
if ((nchar = get_char_from_input_buffer()) == ESC) /* search char */
break;
for (n = DEFAULT1(prenum); n > 0 ;n--) {
if (!search_char(nchar, dir, type)) {
CLEAROP;
beep();
}
}
break;
case ',':
flag = 1;
/* fall through */
case ';':
mtype = MCHAR;
mincl = (1);
set_wanted_cursor_column = (1);
for (n = DEFAULT1(prenum); n > 0 ;n--) {
if (!crep_search(flag)) {
CLEAROP;
beep();
}
}
break;
case '(': /* sentence searches */
dir = SEARCH_BACKWARD;
/* fall through */
case ')':
mtype = MCHAR;
mincl = (0);
set_wanted_cursor_column = (1);
if (find_sent(dir) == 0) {
beep();
CLEAROP;
}
break;
case '{': /* paragraph searches */
dir = SEARCH_BACKWARD;
/* fall through */
case '}':
mtype = MCHAR;
mincl = (0);
set_wanted_cursor_column = (1);
if (!find_param(dir)) {
beep();
CLEAROP;
}
break;
case '[': /* function searches */
dir = SEARCH_BACKWARD;
/* fall through */
case ']':
mtype = MLINE;
set_wanted_cursor_column = (1);
if (get_char_from_input_buffer() != c) {
beep();
CLEAROP;
break;
}
if (!find_function(dir)) {
beep();
CLEAROP;
}
break;
case '%':
mtype = MCHAR;
mincl = (1);
{
LPTR *pos;
if ((pos = show_match()) == NULL) {
beep();
CLEAROP;
} else {
set_pc_mark();
*cursor_char = *pos;
set_wanted_cursor_column = (1);
}
}
break;
/*
* Edits
*/
case '.': /* repeat last change (usually) */
/*
* If a delete is in effect, we let '.' help out the same
* way that '_' helps for some line operations. It's like
* an 'l', but subtracts one from the count and is inclusive.
*/
if (operator == DELETE || operator == CHANGE) {
if (prenum != 0) {
n = DEFAULT1(prenum) - 1;
while (n--)
if (! one_right())
break;
}
mtype = MCHAR;
mincl = (1);
} else { /* a normal 'redo' */
CLEAROP;
put_string_into_input_buffer(redo_buffer);
}
break;
case 'u':
CLEAROP;
u_undo();
break;
case 'U':
CLEAROP;
u_l_undo();
break;
case 'x':
CLEAROP;
if (line_empty()) /* can't do it on a blank line */
beep();
if (prenum)
put_int_into_input_buffer(prenum);
put_string_into_input_buffer("d.");
break;
case 'X':
CLEAROP;
if (!one_left())
beep();
else {
strcpy(redo_buffer, "X");
u_save_line();
delete_char((1));
update_line();
}
break;
case 'r':
CLEAROP;
if (line_empty()) { /* Nothing to replace */
beep();
break;
}
if ((nchar = get_char_from_input_buffer()) == ESC)
break;
if (nchar==CTRL_J || nchar==CTRL_M) {
put_string_into_input_buffer("R\n\033");
break;
}
if (nchar & 0x80) {
beep();
break;
}
u_save_line();
/* Change current character. */
pchar(cursor_char, nchar);
/* Save stuff necessary to redo it */
sprintf(redo_buffer, "r%c", nchar);
CHANGED;
update_line();
break;
case '~': /* swap case */
CLEAROP;
if (line_empty()) {
beep();
break;
}
c = gchar(cursor_char);
if (is_alpha(c)) {
if (is_lower(c))
c = to_upper(c);
else
c = to_lower(c);
}
u_save_line();
pchar(cursor_char, c); /* Change current character. */
one_right();
strcpy(redo_buffer, "~");
CHANGED;
update_line();
break;
case 'J':
CLEAROP;
u_save(cursor_char->linep->prev, cursor_char->linep->next->next);
if (!do_join((1)))
beep();
strcpy(redo_buffer, "J");
update_screen(0);
break;
/*
* Inserts
*/
case 'A':
set_wanted_cursor_column = (1);
while (one_right())
;
/* fall through */
case 'a':
CLEAROP;
/* Works just like an 'i'nsert on the next character. */
if (!line_empty())
inc(cursor_char);
u_save_line();
do_start_insert(mkstr(c), (0));
break;
case 'I':
begin_line((1));
/* fall through */
case 'i':
CLEAROP;
u_save_line();
do_start_insert(mkstr(c), (0));
break;
case 'o':
CLEAROP;
u_save(cursor_char->linep, cursor_char->linep->next);
open_command(SEARCH_FORWARD, (1));
do_start_insert("o", (1));
break;
case 'O':
CLEAROP;
u_save(cursor_char->linep->prev, cursor_char->linep);
open_command(SEARCH_BACKWARD, (1));
do_start_insert("O", (1));
break;
case 'R':
CLEAROP;
u_save_line();
do_start_insert("R", (0));
break;
/*
* Operators
*/
case 'd':
if (operator == DELETE) /* handle 'dd' */
goto lineop;
if (prenum != 0)
opnum = prenum;
startop = *cursor_char;
operator = DELETE;
break;
case 'c':
if (operator == CHANGE) /* handle 'cc' */
goto lineop;
if (prenum != 0)
opnum = prenum;
startop = *cursor_char;
operator = CHANGE;
break;
case 'y':
if (operator == YANK) /* handle 'yy' */
goto lineop;
if (prenum != 0)
opnum = prenum;
startop = *cursor_char;
operator = YANK;
break;
case '>':
if (operator == RSHIFT) /* handle >> */
goto lineop;
if (prenum != 0)
opnum = prenum;
startop = *cursor_char;
operator = RSHIFT;
break;
case '<':
if (operator == LSHIFT) /* handle << */
goto lineop;
if (prenum != 0)
opnum = prenum;
startop = *cursor_char; /* save current position */
operator = LSHIFT;
break;
case '!':
if (operator == FILTER) /* handle '!!' */
goto lineop;
if (prenum != 0)
opnum = prenum;
startop = *cursor_char;
operator = FILTER;
break;
case 'p':
do_put(SEARCH_FORWARD);
break;
case 'P':
do_put(SEARCH_BACKWARD);
break;
/*
* Abbreviations
*/
case 'D':
put_string_into_input_buffer("d$");
break;
case 'Y':
if (prenum)
put_int_into_input_buffer(prenum);
put_string_into_input_buffer("yy");
break;
case 'C':
put_string_into_input_buffer("c$");
break;
case 's': /* substitute characters */
if (prenum)
put_int_into_input_buffer(prenum);
put_string_into_input_buffer("c.");
break;
/*
* Marks
*/
case 'm':
CLEAROP;
if (!set_mark(get_char_from_input_buffer()))
beep();
break;
case '\'':
flag = (1);
/* fall through */
case '`':
{
LPTR mtmp, *mark;
mark = get_mark(get_char_from_input_buffer());
if (mark == NULL) {
beep();
CLEAROP;
} else {
mtmp = *mark;
set_pc_mark();
*cursor_char = mtmp;
if (flag)
begin_line((1));
}
mtype = flag ? MLINE : MCHAR;
mincl = (0); /* ignored if not MCHAR */
set_wanted_cursor_column = (1);
}
break;
default:
CLEAROP;
beep();
break;
}
/*
* If an operation is pending, handle it...
*/
if (finish_op) { /* we just finished an operator */
if (operator == NOP) /* ... but it was cancelled */
return;
switch (operator) {
case LSHIFT:
case RSHIFT:
do_shift(operator, c, nchar, prenum);
break;
case DELETE:
do_delete(c, nchar, prenum);
break;
case YANK:
(void) do_yank(); /* no redo on yank... */
break;
case CHANGE:
do_change(c, nchar, prenum);
break;
case FILTER:
do_filter(c, nchar, prenum);
break;
default:
beep();
}
operator = NOP;
}
}