mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-12-22 14:30:29 +00:00
1204 lines
21 KiB
C
1204 lines
21 KiB
C
|
/*
|
||
|
* User-level command processor.
|
||
|
*/
|
||
|
#pragma noroot
|
||
|
#include "less.h"
|
||
|
#include "position.h"
|
||
|
#include "option.h"
|
||
|
#include "cmd.h"
|
||
|
|
||
|
#ifdef _ORCAC_
|
||
|
segment "LoadSegONE";
|
||
|
#endif
|
||
|
|
||
|
#define NO_MCA 0
|
||
|
#define MCA_DONE 1
|
||
|
#define MCA_MORE 2
|
||
|
|
||
|
extern int erase_char, kill_char;
|
||
|
extern int ispipe;
|
||
|
extern int sigs;
|
||
|
extern int quit_at_eof;
|
||
|
extern int hit_eof;
|
||
|
extern int sc_width;
|
||
|
extern int sc_height;
|
||
|
extern int swindow;
|
||
|
extern int jump_sline;
|
||
|
extern int quitting;
|
||
|
extern int scroll;
|
||
|
extern int nohelp;
|
||
|
extern int ignore_eoi;
|
||
|
extern char *every_first_cmd;
|
||
|
extern char version[];
|
||
|
extern struct scrpos initial_scrpos;
|
||
|
extern IFILE curr_ifile;
|
||
|
#if EDITOR
|
||
|
extern char *editor;
|
||
|
extern char *editproto;
|
||
|
#endif
|
||
|
extern int screen_trashed; /* The screen has been overwritten */
|
||
|
|
||
|
static char ungot[100];
|
||
|
static char *ungotp = NULL;
|
||
|
#if SHELL_ESCAPE
|
||
|
static char *shellcmd = NULL; /* For holding last shell command for "!!" */
|
||
|
#endif
|
||
|
static int mca; /* The multicharacter command (action) */
|
||
|
static int search_type; /* The previous type of search */
|
||
|
static int number; /* The number typed by the user */
|
||
|
static char optchar;
|
||
|
static int optflag;
|
||
|
#if PIPEC
|
||
|
static char pipec;
|
||
|
#endif
|
||
|
|
||
|
static void multi_search(char *pattern, int n);
|
||
|
static void cmd_exec(void);
|
||
|
static void start_mca(int action, char *prompt);
|
||
|
static void mca_search(void);
|
||
|
static void exec_mca(void);
|
||
|
static int mca_char(int c);
|
||
|
static void prompt(void);
|
||
|
static int getcc(void);
|
||
|
|
||
|
/*
|
||
|
* Move the cursor to lower left before executing a command.
|
||
|
* This looks nicer if the command takes a long time before
|
||
|
* updating the screen.
|
||
|
*/
|
||
|
static void
|
||
|
cmd_exec(void)
|
||
|
{
|
||
|
lower_left();
|
||
|
flush();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Set up the display to start a new multi-character command.
|
||
|
*/
|
||
|
static void
|
||
|
start_mca(action, prompt)
|
||
|
int action;
|
||
|
char *prompt;
|
||
|
{
|
||
|
mca = action;
|
||
|
lower_left();
|
||
|
clear_eol();
|
||
|
cmd_putstr(prompt);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Set up the display to start a new search command.
|
||
|
*/
|
||
|
static void
|
||
|
mca_search(void)
|
||
|
{
|
||
|
switch (SRCH_DIR(search_type))
|
||
|
{
|
||
|
case SRCH_FORW:
|
||
|
mca = A_F_SEARCH;
|
||
|
break;
|
||
|
case SRCH_BACK:
|
||
|
mca = A_B_SEARCH;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
lower_left();
|
||
|
clear_eol();
|
||
|
|
||
|
if (search_type & SRCH_FIRST_FILE)
|
||
|
cmd_putstr("@");
|
||
|
|
||
|
if (search_type & SRCH_PAST_EOF)
|
||
|
cmd_putstr("*");
|
||
|
|
||
|
if (search_type & SRCH_NOMATCH)
|
||
|
cmd_putstr("!");
|
||
|
|
||
|
switch (SRCH_DIR(search_type))
|
||
|
{
|
||
|
case SRCH_FORW:
|
||
|
cmd_putstr("/");
|
||
|
break;
|
||
|
case SRCH_BACK:
|
||
|
cmd_putstr("?");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Execute a multicharacter command.
|
||
|
*/
|
||
|
static void
|
||
|
exec_mca(void)
|
||
|
{
|
||
|
register char *cbuf;
|
||
|
register char *s;
|
||
|
|
||
|
cmd_exec();
|
||
|
cbuf = get_cmdbuf();
|
||
|
|
||
|
switch (mca)
|
||
|
{
|
||
|
case A_F_SEARCH:
|
||
|
case A_B_SEARCH:
|
||
|
multi_search(cbuf, number);
|
||
|
break;
|
||
|
case A_FIRSTCMD:
|
||
|
/*
|
||
|
* Skip leading spaces or + signs in the string.
|
||
|
*/
|
||
|
while (*cbuf == '+' || *cbuf == ' ')
|
||
|
cbuf++;
|
||
|
if (every_first_cmd != NULL)
|
||
|
free(every_first_cmd);
|
||
|
if (*cbuf == '\0')
|
||
|
every_first_cmd = NULL;
|
||
|
else
|
||
|
every_first_cmd = save(cbuf);
|
||
|
break;
|
||
|
case A_OPT_TOGGLE:
|
||
|
toggle_option(optchar, cbuf, optflag);
|
||
|
optchar = '\0';
|
||
|
break;
|
||
|
case A_F_BRACKET:
|
||
|
match_brac(cbuf[0], cbuf[1], 1, number);
|
||
|
break;
|
||
|
case A_B_BRACKET:
|
||
|
match_brac(cbuf[1], cbuf[0], 0, number);
|
||
|
break;
|
||
|
case A_EXAMINE:
|
||
|
/*
|
||
|
* Ignore leading spaces and glob the filename.
|
||
|
*/
|
||
|
cbuf = skipsp(cbuf);
|
||
|
s = glob(cbuf);
|
||
|
if (s != NULL)
|
||
|
{
|
||
|
edit_list(s);
|
||
|
free(s);
|
||
|
} else
|
||
|
edit_list(cbuf);
|
||
|
break;
|
||
|
#if SHELL_ESCAPE
|
||
|
case A_SHELL:
|
||
|
/*
|
||
|
* !! just uses whatever is in shellcmd.
|
||
|
* Otherwise, copy cmdbuf to shellcmd,
|
||
|
* expanding any special characters ("%" or "#").
|
||
|
*/
|
||
|
if (*cbuf != '!')
|
||
|
{
|
||
|
if (shellcmd != NULL)
|
||
|
free(shellcmd);
|
||
|
shellcmd = fexpand(cbuf);
|
||
|
if (shellcmd == NULL)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (shellcmd == NULL)
|
||
|
lsystem("");
|
||
|
else
|
||
|
lsystem(shellcmd);
|
||
|
error("!done", NULL_PARG);
|
||
|
break;
|
||
|
#endif
|
||
|
#if PIPEC
|
||
|
case A_PIPE:
|
||
|
(void) pipe_mark(pipec, cbuf);
|
||
|
error("|done", NULL_PARG);
|
||
|
break;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Add a character to a multi-character command.
|
||
|
*/
|
||
|
static int
|
||
|
mca_char(c)
|
||
|
int c;
|
||
|
{
|
||
|
char *p;
|
||
|
int flag;
|
||
|
char buf[3];
|
||
|
|
||
|
switch (mca)
|
||
|
{
|
||
|
case 0:
|
||
|
/*
|
||
|
* Not in a multicharacter command.
|
||
|
*/
|
||
|
return (NO_MCA);
|
||
|
|
||
|
case A_PREFIX:
|
||
|
/*
|
||
|
* In the prefix of a command.
|
||
|
* This not considered a multichar command
|
||
|
* (even tho it uses cmdbuf, etc.).
|
||
|
* It is handled in the commands() switch.
|
||
|
*/
|
||
|
return (NO_MCA);
|
||
|
|
||
|
case A_DIGIT:
|
||
|
/*
|
||
|
* Entering digits of a number.
|
||
|
* Terminated by a non-digit.
|
||
|
*/
|
||
|
if ((c < '0' || c > '9') &&
|
||
|
c != erase_char && c != kill_char)
|
||
|
{
|
||
|
/*
|
||
|
* Not part of the number.
|
||
|
* Treat as a normal command character.
|
||
|
*/
|
||
|
number = cmd_int();
|
||
|
mca = 0;
|
||
|
return (NO_MCA);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case A_OPT_TOGGLE:
|
||
|
/*
|
||
|
* Special case for the TOGGLE_OPTION command.
|
||
|
* If the option letter which was entered is a
|
||
|
* single-char option, execute the command immediately,
|
||
|
* so user doesn't have to hit RETURN.
|
||
|
* If the first char is + or -, this indicates
|
||
|
* OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
|
||
|
*/
|
||
|
if (c == erase_char || c == kill_char)
|
||
|
break;
|
||
|
if (optchar != '\0' && optchar != '+' && optchar != '-')
|
||
|
/*
|
||
|
* We already have the option letter.
|
||
|
*/
|
||
|
break;
|
||
|
switch (c)
|
||
|
{
|
||
|
case '+':
|
||
|
optflag = OPT_UNSET;
|
||
|
break;
|
||
|
case '-':
|
||
|
optflag = OPT_SET;
|
||
|
break;
|
||
|
default:
|
||
|
optchar = c;
|
||
|
if (optflag != OPT_TOGGLE || single_char_option(c))
|
||
|
{
|
||
|
toggle_option(c, "", optflag);
|
||
|
return (MCA_DONE);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (optchar == '+' || optchar == '-')
|
||
|
{
|
||
|
optchar = c;
|
||
|
break;
|
||
|
}
|
||
|
/*
|
||
|
* Display a prompt appropriate for the option letter.
|
||
|
*/
|
||
|
if ((p = opt_prompt(c)) == NULL)
|
||
|
{
|
||
|
buf[0] = '-';
|
||
|
buf[1] = c;
|
||
|
buf[2] = '\0';
|
||
|
p = buf;
|
||
|
}
|
||
|
start_mca(A_OPT_TOGGLE, p);
|
||
|
return (MCA_MORE);
|
||
|
|
||
|
case A_F_SEARCH:
|
||
|
case A_B_SEARCH:
|
||
|
/*
|
||
|
* Special case for search commands.
|
||
|
* Certain characters as the first char of
|
||
|
* the pattern have special meaning:
|
||
|
* ! Toggle the NOMATCH flag
|
||
|
* * Toggle the PAST_EOF flag
|
||
|
* @ Toggle the FIRST_FILE flag
|
||
|
*/
|
||
|
if (len_cmdbuf() > 0)
|
||
|
/*
|
||
|
* Only works for the first char of the pattern.
|
||
|
*/
|
||
|
break;
|
||
|
|
||
|
flag = 0;
|
||
|
switch (c)
|
||
|
{
|
||
|
case '!':
|
||
|
flag = SRCH_NOMATCH;
|
||
|
break;
|
||
|
case '@':
|
||
|
flag = SRCH_FIRST_FILE;
|
||
|
break;
|
||
|
case '*':
|
||
|
flag = SRCH_PAST_EOF;
|
||
|
break;
|
||
|
}
|
||
|
if (flag != 0)
|
||
|
{
|
||
|
search_type ^= flag;
|
||
|
mca_search();
|
||
|
return (MCA_MORE);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Any other multicharacter command
|
||
|
* is terminated by a newline.
|
||
|
*/
|
||
|
if (c == '\n' || c == '\r')
|
||
|
{
|
||
|
/*
|
||
|
* Execute the command.
|
||
|
*/
|
||
|
exec_mca();
|
||
|
return (MCA_DONE);
|
||
|
}
|
||
|
/*
|
||
|
* Append the char to the command buffer.
|
||
|
*/
|
||
|
if (cmd_char(c))
|
||
|
/*
|
||
|
* Abort the multi-char command.
|
||
|
*/
|
||
|
return (MCA_DONE);
|
||
|
|
||
|
if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
|
||
|
{
|
||
|
/*
|
||
|
* Special case for the bracket-matching commands.
|
||
|
* Execute the command after getting exactly two
|
||
|
* characters from the user.
|
||
|
*/
|
||
|
exec_mca();
|
||
|
return (MCA_DONE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Need another character.
|
||
|
*/
|
||
|
return (MCA_MORE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Display the appropriate prompt.
|
||
|
*/
|
||
|
static void
|
||
|
prompt(void)
|
||
|
{
|
||
|
register char *p;
|
||
|
|
||
|
if (ungotp != NULL && ungotp > ungot)
|
||
|
{
|
||
|
/*
|
||
|
* No prompt necessary if commands are from
|
||
|
* ungotten chars rather than from the user.
|
||
|
*/
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If nothing is displayed yet, display starting from initial_scrpos.
|
||
|
*/
|
||
|
if (empty_screen())
|
||
|
{
|
||
|
if (initial_scrpos.pos == NULL_POSITION)
|
||
|
/*
|
||
|
* {{ Maybe this should be:
|
||
|
* jump_loc(ch_zero(), jump_sline);
|
||
|
* but this behavior seems rather unexpected
|
||
|
* on the first screen. }}
|
||
|
*/
|
||
|
jump_loc(ch_zero(), 1);
|
||
|
else
|
||
|
jump_loc(initial_scrpos.pos, initial_scrpos.ln);
|
||
|
} else if (screen_trashed)
|
||
|
repaint();
|
||
|
|
||
|
/*
|
||
|
* If the -E flag is set and we've hit EOF on the last file, quit.
|
||
|
*/
|
||
|
if (quit_at_eof == 2 && hit_eof &&
|
||
|
next_ifile(curr_ifile) == NULL_IFILE)
|
||
|
quit(0);
|
||
|
|
||
|
/*
|
||
|
* Select the proper prompt and display it.
|
||
|
*/
|
||
|
lower_left();
|
||
|
clear_eol();
|
||
|
p = pr_string();
|
||
|
if (p == NULL)
|
||
|
putchr(':');
|
||
|
else
|
||
|
{
|
||
|
so_enter();
|
||
|
putstr(p);
|
||
|
so_exit();
|
||
|
}
|
||
|
#if __MSDOS__
|
||
|
scroll_bar();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get command character.
|
||
|
* The character normally comes from the keyboard,
|
||
|
* but may come from ungotten characters
|
||
|
* (characters previously given to ungetcc or ungetsc).
|
||
|
*/
|
||
|
static int
|
||
|
getcc(void)
|
||
|
{
|
||
|
if (ungotp == NULL)
|
||
|
/*
|
||
|
* Normal case: no ungotten chars, so get one from the user.
|
||
|
*/
|
||
|
return (getchr());
|
||
|
|
||
|
if (ungotp > ungot)
|
||
|
/*
|
||
|
* Return the next ungotten char.
|
||
|
*/
|
||
|
return (*--ungotp);
|
||
|
|
||
|
/*
|
||
|
* We have just run out of ungotten chars.
|
||
|
*/
|
||
|
ungotp = NULL;
|
||
|
if (len_cmdbuf() == 0 || !empty_screen())
|
||
|
return (getchr());
|
||
|
/*
|
||
|
* Command is incomplete, so try to complete it.
|
||
|
*/
|
||
|
switch (mca)
|
||
|
{
|
||
|
case A_DIGIT:
|
||
|
/*
|
||
|
* We have a number but no command. Treat as #g.
|
||
|
*/
|
||
|
return ('g');
|
||
|
|
||
|
case A_F_SEARCH:
|
||
|
case A_B_SEARCH:
|
||
|
/*
|
||
|
* We have "/string" but no newline. Add the \n.
|
||
|
*/
|
||
|
return ('\n');
|
||
|
|
||
|
default:
|
||
|
/*
|
||
|
* Some other incomplete command. Let user complete it.
|
||
|
*/
|
||
|
return (getchr());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* "Unget" a command character.
|
||
|
* The next getcc() will return this character.
|
||
|
*/
|
||
|
public void
|
||
|
ungetcc(c)
|
||
|
int c;
|
||
|
{
|
||
|
if (ungotp == NULL)
|
||
|
ungotp = ungot;
|
||
|
if (ungotp >= ungot + sizeof(ungot))
|
||
|
{
|
||
|
error("ungetcc overflow", NULL_PARG);
|
||
|
quit(1);
|
||
|
}
|
||
|
*ungotp++ = c;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Unget a whole string of command characters.
|
||
|
* The next sequence of getcc()'s will return this string.
|
||
|
*/
|
||
|
public void
|
||
|
ungetsc(s)
|
||
|
char *s;
|
||
|
{
|
||
|
register char *p;
|
||
|
|
||
|
for (p = s + strlen(s) - 1; p >= s; p--)
|
||
|
ungetcc(*p);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Search for a pattern, possibly in multiple files.
|
||
|
* If SRCH_FIRST_FILE is set, begin searching at the first file.
|
||
|
* If SRCH_PAST_EOF is set, continue the search thru multiple files.
|
||
|
*/
|
||
|
static void
|
||
|
multi_search(pattern, n)
|
||
|
char *pattern;
|
||
|
int n;
|
||
|
{
|
||
|
register int nomore;
|
||
|
char *curr_filename;
|
||
|
int changed_file;
|
||
|
|
||
|
changed_file = 0;
|
||
|
curr_filename = get_filename(curr_ifile);
|
||
|
|
||
|
if (search_type & SRCH_FIRST_FILE)
|
||
|
{
|
||
|
/*
|
||
|
* Start at the first (or last) file
|
||
|
* in the command line list.
|
||
|
*/
|
||
|
if (SRCH_DIR(search_type) == SRCH_FORW)
|
||
|
nomore = edit_first();
|
||
|
else
|
||
|
nomore = edit_last();
|
||
|
if (nomore)
|
||
|
return;
|
||
|
changed_file = 1;
|
||
|
search_type &= ~SRCH_FIRST_FILE;
|
||
|
}
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
if ((n = search(search_type, pattern, n)) == 0)
|
||
|
/*
|
||
|
* Found it.
|
||
|
*/
|
||
|
return;
|
||
|
|
||
|
if (n < 0)
|
||
|
/*
|
||
|
* Some kind of error in the search.
|
||
|
* Error message has been printed by search().
|
||
|
*/
|
||
|
break;
|
||
|
|
||
|
if ((search_type & SRCH_PAST_EOF) == 0)
|
||
|
/*
|
||
|
* We didn't find a match, but we're
|
||
|
* supposed to search only one file.
|
||
|
*/
|
||
|
break;
|
||
|
/*
|
||
|
* Move on to the next file.
|
||
|
*/
|
||
|
if (SRCH_DIR(search_type) == SRCH_BACK)
|
||
|
nomore = edit_prev(1);
|
||
|
else
|
||
|
nomore = edit_next(1);
|
||
|
if (nomore)
|
||
|
break;
|
||
|
changed_file = 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Didn't find it.
|
||
|
* Print an error message if we haven't already.
|
||
|
*/
|
||
|
if (n > 0)
|
||
|
error("Pattern not found", NULL_PARG);
|
||
|
|
||
|
if (changed_file)
|
||
|
/*
|
||
|
* Restore the file we were originally viewing.
|
||
|
*/
|
||
|
(void) edit(curr_filename, 0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Main command processor.
|
||
|
* Accept and execute commands until a quit command.
|
||
|
*/
|
||
|
public void
|
||
|
commands(void)
|
||
|
{
|
||
|
register int c;
|
||
|
register int action;
|
||
|
register char *cbuf;
|
||
|
int save_search_type;
|
||
|
char *s;
|
||
|
char tbuf[2];
|
||
|
PARG parg;
|
||
|
|
||
|
search_type = SRCH_FORW;
|
||
|
scroll = (sc_height + 1) / 2;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
mca = 0;
|
||
|
number = 0;
|
||
|
optchar = '\0';
|
||
|
|
||
|
/*
|
||
|
* See if any signals need processing.
|
||
|
*/
|
||
|
if (sigs)
|
||
|
{
|
||
|
psignals();
|
||
|
if (quitting)
|
||
|
quit(-1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Display prompt and accept a character.
|
||
|
*/
|
||
|
cmd_reset();
|
||
|
prompt();
|
||
|
if (sigs)
|
||
|
continue;
|
||
|
c = getcc();
|
||
|
|
||
|
again:
|
||
|
if (sigs)
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* If we are in a multicharacter command, call mca_char.
|
||
|
* Otherwise we call cmd_decode to determine the
|
||
|
* action to be performed.
|
||
|
*/
|
||
|
if (mca)
|
||
|
switch (mca_char(c))
|
||
|
{
|
||
|
case MCA_MORE:
|
||
|
/*
|
||
|
* Need another character.
|
||
|
*/
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
case MCA_DONE:
|
||
|
/*
|
||
|
* Command has been handled by mca_char.
|
||
|
* Start clean with a prompt.
|
||
|
*/
|
||
|
continue;
|
||
|
case NO_MCA:
|
||
|
/*
|
||
|
* Not a multi-char command
|
||
|
* (at least, not anymore).
|
||
|
*/
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Decode the command character and decide what to do.
|
||
|
*/
|
||
|
if (mca)
|
||
|
{
|
||
|
/*
|
||
|
* We're in a multichar command.
|
||
|
* Add the character to the command buffer
|
||
|
* and display it on the screen.
|
||
|
* If the user backspaces past the start
|
||
|
* of the line, abort the command.
|
||
|
*/
|
||
|
if (cmd_char(c) || len_cmdbuf() == 0)
|
||
|
continue;
|
||
|
cbuf = get_cmdbuf();
|
||
|
} else
|
||
|
{
|
||
|
/*
|
||
|
* Don't use cmd_char if we're starting fresh
|
||
|
* at the beginning of a command, because we
|
||
|
* don't want to echo the command until we know
|
||
|
* it is a multichar command. We also don't
|
||
|
* want erase_char/kill_char to be treated
|
||
|
* as line editing characters.
|
||
|
*/
|
||
|
tbuf[0] = c;
|
||
|
tbuf[1] = '\0';
|
||
|
cbuf = tbuf;
|
||
|
}
|
||
|
s = NULL;
|
||
|
action = cmd_decode(cbuf, &s);
|
||
|
/*
|
||
|
* If an "extra" string was returned,
|
||
|
* process it as a string of command characters.
|
||
|
*/
|
||
|
if (s != NULL)
|
||
|
ungetsc(s);
|
||
|
/*
|
||
|
* Clear the cmdbuf string.
|
||
|
* (But not if we're in the prefix of a command,
|
||
|
* because the partial command string is kept there.)
|
||
|
*/
|
||
|
if (action != A_PREFIX)
|
||
|
cmd_reset();
|
||
|
|
||
|
switch (action)
|
||
|
{
|
||
|
case A_DIGIT:
|
||
|
/*
|
||
|
* First digit of a number.
|
||
|
*/
|
||
|
start_mca(A_DIGIT, ":");
|
||
|
goto again;
|
||
|
|
||
|
case A_F_WINDOW:
|
||
|
/*
|
||
|
* Forward one window (and set the window size).
|
||
|
*/
|
||
|
if (number > 0)
|
||
|
swindow = number;
|
||
|
/* FALLTHRU */
|
||
|
case A_F_SCREEN:
|
||
|
/*
|
||
|
* Forward one screen.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = swindow;
|
||
|
cmd_exec();
|
||
|
forward(number, 0, 1);
|
||
|
break;
|
||
|
|
||
|
case A_B_WINDOW:
|
||
|
/*
|
||
|
* Backward one window (and set the window size).
|
||
|
*/
|
||
|
if (number > 0)
|
||
|
swindow = number;
|
||
|
/* FALLTHRU */
|
||
|
case A_B_SCREEN:
|
||
|
/*
|
||
|
* Backward one screen.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = swindow;
|
||
|
cmd_exec();
|
||
|
backward(number, 0, 1);
|
||
|
break;
|
||
|
|
||
|
case A_F_LINE:
|
||
|
/*
|
||
|
* Forward N (default 1) line.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
cmd_exec();
|
||
|
forward(number, 0, 0);
|
||
|
break;
|
||
|
|
||
|
case A_B_LINE:
|
||
|
/*
|
||
|
* Backward N (default 1) line.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
cmd_exec();
|
||
|
backward(number, 0, 0);
|
||
|
break;
|
||
|
|
||
|
case A_FF_LINE:
|
||
|
/*
|
||
|
* Force forward N (default 1) line.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
cmd_exec();
|
||
|
forward(number, 1, 0);
|
||
|
break;
|
||
|
|
||
|
case A_BF_LINE:
|
||
|
/*
|
||
|
* Force backward N (default 1) line.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
cmd_exec();
|
||
|
backward(number, 1, 0);
|
||
|
break;
|
||
|
|
||
|
case A_F_FOREVER:
|
||
|
/*
|
||
|
* Forward forever, ignoring EOF.
|
||
|
*/
|
||
|
cmd_exec();
|
||
|
jump_forw();
|
||
|
ignore_eoi = 1;
|
||
|
hit_eof = 0;
|
||
|
while (sigs == 0)
|
||
|
forward(1, 0, 0);
|
||
|
ignore_eoi = 0;
|
||
|
break;
|
||
|
|
||
|
case A_F_SCROLL:
|
||
|
/*
|
||
|
* Forward N lines
|
||
|
* (default same as last 'd' or 'u' command).
|
||
|
*/
|
||
|
if (number > 0)
|
||
|
scroll = number;
|
||
|
cmd_exec();
|
||
|
forward(scroll, 0, 0);
|
||
|
break;
|
||
|
|
||
|
case A_B_SCROLL:
|
||
|
/*
|
||
|
* Forward N lines
|
||
|
* (default same as last 'd' or 'u' command).
|
||
|
*/
|
||
|
if (number > 0)
|
||
|
scroll = number;
|
||
|
cmd_exec();
|
||
|
backward(scroll, 0, 0);
|
||
|
break;
|
||
|
|
||
|
case A_FREPAINT:
|
||
|
/*
|
||
|
* Flush buffers, then repaint screen.
|
||
|
* Don't flush the buffers on a pipe!
|
||
|
*/
|
||
|
ch_flush();
|
||
|
if (!ispipe)
|
||
|
clr_linenum();
|
||
|
/* FALLTHRU */
|
||
|
case A_REPAINT:
|
||
|
/*
|
||
|
* Repaint screen.
|
||
|
*/
|
||
|
cmd_exec();
|
||
|
repaint();
|
||
|
break;
|
||
|
|
||
|
case A_GOLINE:
|
||
|
/*
|
||
|
* Go to line N, default beginning of file.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
cmd_exec();
|
||
|
jump_back(number);
|
||
|
break;
|
||
|
|
||
|
case A_PERCENT:
|
||
|
/*
|
||
|
* Go to a specified percentage into the file.
|
||
|
*/
|
||
|
if (number < 0)
|
||
|
number = 0;
|
||
|
if (number > 100)
|
||
|
number = 100;
|
||
|
cmd_exec();
|
||
|
jump_percent(number);
|
||
|
break;
|
||
|
|
||
|
case A_GOEND:
|
||
|
/*
|
||
|
* Go to line N, default end of file.
|
||
|
*/
|
||
|
cmd_exec();
|
||
|
if (number <= 0)
|
||
|
jump_forw();
|
||
|
else
|
||
|
jump_back(number);
|
||
|
break;
|
||
|
|
||
|
case A_GOPOS:
|
||
|
/*
|
||
|
* Go to a specified byte position in the file.
|
||
|
*/
|
||
|
cmd_exec();
|
||
|
if (number < 0)
|
||
|
number = 0;
|
||
|
jump_line_loc((POSITION)number, jump_sline);
|
||
|
break;
|
||
|
|
||
|
case A_STAT:
|
||
|
/*
|
||
|
* Print file name, etc.
|
||
|
*/
|
||
|
cmd_exec();
|
||
|
parg.p_string = eq_message();
|
||
|
error("%s", &parg);
|
||
|
break;
|
||
|
|
||
|
case A_VERSION:
|
||
|
/*
|
||
|
* Print version number, without the "@(#)".
|
||
|
*/
|
||
|
cmd_exec();
|
||
|
parg.p_string = version+4;
|
||
|
error("%s", &parg);
|
||
|
break;
|
||
|
|
||
|
case A_QUIT:
|
||
|
/*
|
||
|
* Exit.
|
||
|
*/
|
||
|
quit(0);
|
||
|
|
||
|
/*
|
||
|
* Define abbreviation for a commonly used sequence below.
|
||
|
*/
|
||
|
#define DO_SEARCH() if (number <= 0) number = 1; \
|
||
|
mca_search(); \
|
||
|
cmd_exec(); \
|
||
|
multi_search((char *)NULL, number);
|
||
|
|
||
|
|
||
|
case A_F_SEARCH:
|
||
|
/*
|
||
|
* Search forward for a pattern.
|
||
|
* Get the first char of the pattern.
|
||
|
*/
|
||
|
search_type = SRCH_FORW;
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
mca_search();
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
|
||
|
case A_B_SEARCH:
|
||
|
/*
|
||
|
* Search backward for a pattern.
|
||
|
* Get the first char of the pattern.
|
||
|
*/
|
||
|
search_type = SRCH_BACK;
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
mca_search();
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
|
||
|
case A_AGAIN_SEARCH:
|
||
|
/*
|
||
|
* Repeat previous search.
|
||
|
*/
|
||
|
DO_SEARCH();
|
||
|
break;
|
||
|
|
||
|
case A_T_AGAIN_SEARCH:
|
||
|
/*
|
||
|
* Repeat previous search, multiple files.
|
||
|
*/
|
||
|
search_type |= SRCH_PAST_EOF;
|
||
|
DO_SEARCH();
|
||
|
break;
|
||
|
|
||
|
case A_REVERSE_SEARCH:
|
||
|
/*
|
||
|
* Repeat previous search, in reverse direction.
|
||
|
*/
|
||
|
save_search_type = search_type;
|
||
|
search_type = SRCH_REVERSE(search_type);
|
||
|
DO_SEARCH();
|
||
|
search_type = save_search_type;
|
||
|
break;
|
||
|
|
||
|
case A_T_REVERSE_SEARCH:
|
||
|
/*
|
||
|
* Repeat previous search,
|
||
|
* multiple files in reverse direction.
|
||
|
*/
|
||
|
save_search_type = search_type;
|
||
|
search_type = SRCH_REVERSE(search_type);
|
||
|
search_type |= SRCH_PAST_EOF;
|
||
|
DO_SEARCH();
|
||
|
search_type = save_search_type;
|
||
|
break;
|
||
|
|
||
|
case A_HELP:
|
||
|
/*
|
||
|
* Help.
|
||
|
*/
|
||
|
if (nohelp)
|
||
|
{
|
||
|
bell();
|
||
|
break;
|
||
|
}
|
||
|
lower_left();
|
||
|
clear_eol();
|
||
|
putstr("help");
|
||
|
cmd_exec();
|
||
|
help();
|
||
|
break;
|
||
|
|
||
|
case A_EXAMINE:
|
||
|
/*
|
||
|
* Edit a new file. Get the filename.
|
||
|
*/
|
||
|
start_mca(A_EXAMINE, "Examine: ");
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
|
||
|
case A_VISUAL:
|
||
|
/*
|
||
|
* Invoke an editor on the input file.
|
||
|
*/
|
||
|
#if EDITOR
|
||
|
if (strcmp(get_filename(curr_ifile), "-") == 0)
|
||
|
{
|
||
|
error("Cannot edit standard input", NULL_PARG);
|
||
|
break;
|
||
|
}
|
||
|
/*
|
||
|
* Expand the editor prototype string
|
||
|
* and pass it to the system to execute.
|
||
|
*/
|
||
|
cmd_exec();
|
||
|
lsystem(pr_expand(editproto, 0));
|
||
|
/*
|
||
|
* Re-edit the file, since data may have changed.
|
||
|
* Some editors even recreate the file, so flushing
|
||
|
* buffers is not sufficient.
|
||
|
*/
|
||
|
(void) edit(get_filename(curr_ifile), 0);
|
||
|
break;
|
||
|
#else
|
||
|
error("Command not available", NULL_PARG);
|
||
|
break;
|
||
|
#endif
|
||
|
|
||
|
case A_NEXT_FILE:
|
||
|
/*
|
||
|
* Examine next file.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
if (edit_next(number))
|
||
|
{
|
||
|
if (quit_at_eof && hit_eof)
|
||
|
quit(0);
|
||
|
parg.p_string = (number > 1) ? "(N-th) " : "";
|
||
|
error("No %snext file", &parg);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case A_PREV_FILE:
|
||
|
/*
|
||
|
* Examine previous file.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
if (edit_prev(number))
|
||
|
{
|
||
|
parg.p_string = (number > 1) ? "(N-th) " : "";
|
||
|
error("No %sprevious file", &parg);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case A_INDEX_FILE:
|
||
|
/*
|
||
|
* Examine a particular file.
|
||
|
*/
|
||
|
if (number <= 0)
|
||
|
number = 1;
|
||
|
if (edit_index(number))
|
||
|
error("No such file", NULL_PARG);
|
||
|
break;
|
||
|
|
||
|
case A_OPT_TOGGLE:
|
||
|
start_mca(A_OPT_TOGGLE, "-");
|
||
|
optflag = OPT_TOGGLE;
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
|
||
|
case A_DISP_OPTION:
|
||
|
/*
|
||
|
* Report a flag setting.
|
||
|
*/
|
||
|
start_mca(A_DISP_OPTION, "_");
|
||
|
c = getcc();
|
||
|
if (c == erase_char || c == kill_char)
|
||
|
break;
|
||
|
toggle_option(c, "", OPT_NO_TOGGLE);
|
||
|
break;
|
||
|
|
||
|
case A_FIRSTCMD:
|
||
|
/*
|
||
|
* Set an initial command for new files.
|
||
|
*/
|
||
|
start_mca(A_FIRSTCMD, "+");
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
|
||
|
case A_SHELL:
|
||
|
/*
|
||
|
* Shell escape.
|
||
|
*/
|
||
|
#if SHELL_ESCAPE
|
||
|
start_mca(A_SHELL, "!");
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
#else
|
||
|
error("Command not available", NULL_PARG);
|
||
|
break;
|
||
|
#endif
|
||
|
|
||
|
case A_SETMARK:
|
||
|
/*
|
||
|
* Set a mark.
|
||
|
*/
|
||
|
start_mca(A_SETMARK, "mark: ");
|
||
|
c = getcc();
|
||
|
if (c == erase_char || c == kill_char ||
|
||
|
c == '\n' || c == '\r')
|
||
|
break;
|
||
|
setmark(c);
|
||
|
break;
|
||
|
|
||
|
case A_GOMARK:
|
||
|
/*
|
||
|
* Go to a mark.
|
||
|
*/
|
||
|
start_mca(A_GOMARK, "goto mark: ");
|
||
|
c = getcc();
|
||
|
if (c == erase_char || c == kill_char ||
|
||
|
c == '\n' || c == '\r')
|
||
|
break;
|
||
|
gomark(c);
|
||
|
break;
|
||
|
|
||
|
#if PIPEC
|
||
|
case A_PIPE:
|
||
|
start_mca(A_PIPE, "|mark: ");
|
||
|
c = getcc();
|
||
|
if (c == erase_char || c == kill_char)
|
||
|
break;
|
||
|
if (c == '\n' || c == '\r')
|
||
|
c = '.';
|
||
|
if (badmark(c))
|
||
|
break;
|
||
|
pipec = c;
|
||
|
start_mca(A_PIPE, "!");
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
#endif
|
||
|
|
||
|
case A_B_BRACKET:
|
||
|
case A_F_BRACKET:
|
||
|
start_mca(action, "Brackets: ");
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
|
||
|
case A_PREFIX:
|
||
|
/*
|
||
|
* The command is incomplete (more chars are needed).
|
||
|
* Display the current char, so the user knows
|
||
|
* what's going on, and get another character.
|
||
|
*/
|
||
|
if (mca != A_PREFIX)
|
||
|
{
|
||
|
start_mca(A_PREFIX, " ");
|
||
|
cmd_reset();
|
||
|
(void) cmd_char(c);
|
||
|
}
|
||
|
c = getcc();
|
||
|
goto again;
|
||
|
|
||
|
case A_NOACTION:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
bell();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|