* User-level command processor.
extern int erase_char
, kill_char
;
extern char *every_first_cmd
;
extern struct scrpos initial_scrpos
;
extern int screen_trashed
; /* The screen has been overwritten */
static char *ungotp
= NULL
;
static char *shellcmd
= NULL
; /* For holding last shell command for "!!" */
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 void multi_search();
* Move the cursor to lower left before executing a command.
* This looks nicer if the command takes a long time before
* Set up the display to start a new multi-character command.
start_mca(action
, prompt
)
* Set up the display to start a new search command.
switch (SRCH_DIR(search_type
))
if (search_type
& SRCH_FIRST_FILE
)
if (search_type
& SRCH_PAST_EOF
)
if (search_type
& SRCH_NOMATCH
)
switch (SRCH_DIR(search_type
))
* Execute a multicharacter command.
multi_search(cbuf
, number
);
* Skip leading spaces or + signs in the string.
while (*cbuf
== '+' || *cbuf
== ' ')
if (every_first_cmd
!= NULL
)
every_first_cmd
= save(cbuf
);
toggle_option(optchar
, cbuf
, optflag
);
match_brac(cbuf
[0], cbuf
[1], 1, number
);
match_brac(cbuf
[1], cbuf
[0], 0, number
);
* Ignore leading spaces and glob the filename.
* !! just uses whatever is in shellcmd.
* Otherwise, copy cmdbuf to shellcmd,
* expanding any special characters ("%" or "#").
shellcmd
= fexpand(cbuf
);
error("!done", NULL_PARG
);
(void) pipe_mark(pipec
, cbuf
);
error("|done", NULL_PARG
);
* Add a character to a multi-character command.
* Not in a multicharacter command.
* 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.
* 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.
* 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
)
if (optchar
!= '\0' && optchar
!= '+' && optchar
!= '-')
* We already have the option letter.
if (optflag
!= OPT_TOGGLE
|| single_char_option(c
))
toggle_option(c
, "", optflag
);
if (optchar
== '+' || optchar
== '-')
* Display a prompt appropriate for the option letter.
if ((p
= opt_prompt(c
)) == NULL
)
start_mca(A_OPT_TOGGLE
, p
);
* 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
* Only works for the first char of the pattern.
* Any other multicharacter command
* is terminated by a newline.
if (c
== '\n' || c
== '\r')
* Append the char to the command buffer.
* Abort the multi-char command.
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.
* Need another character.
* Display the appropriate prompt.
if (ungotp
!= NULL
&& ungotp
> ungot
)
* No prompt necessary if commands are from
* ungotten chars rather than from the user.
* If nothing is displayed yet, display starting from initial_scrpos.
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(initial_scrpos
.pos
, initial_scrpos
.ln
);
} else if (screen_trashed
)
* 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
)
* Select the proper prompt and display it.
* The character normally comes from the keyboard,
* but may come from ungotten characters
* (characters previously given to ungetcc or ungetsc).
* Normal case: no ungotten chars, so get one from the user.
* Return the next ungotten char.
* We have just run out of ungotten chars.
if (len_cmdbuf() == 0 || !empty_screen())
* Command is incomplete, so try to complete it.
* We have a number but no command. Treat as #g.
* We have "/string" but no newline. Add the \n.
* Some other incomplete command. Let user complete it.
* "Unget" a command character.
* The next getcc() will return this character.
if (ungotp
>= ungot
+ sizeof(ungot
))
error("ungetcc overflow", NULL_PARG
);
* Unget a whole string of command characters.
* The next sequence of getcc()'s will return this string.
for (p
= s
+ strlen(s
) - 1; p
>= s
; 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.
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
)
search_type
&= ~SRCH_FIRST_FILE
;
if ((n
= search(search_type
, pattern
, n
)) == 0)
* Some kind of error in the search.
* Error message has been printed by search().
if ((search_type
& SRCH_PAST_EOF
) == 0)
* We didn't find a match, but we're
* supposed to search only one file.
* Move on to the next file.
if (SRCH_DIR(search_type
) == SRCH_BACK
)
* Print an error message if we haven't already.
error("Pattern not found", NULL_PARG
);
* Restore the file we were originally viewing.
(void) edit(curr_filename
, 0);
* Main command processor.
* Accept and execute commands until a quit command.
scroll
= (sc_height
+ 1) / 2;
* See if any signals need processing.
* Display prompt and accept a character.
* If we are in a multicharacter command, call mca_char.
* Otherwise we call cmd_decode to determine the
* action to be performed.
* Need another character.
* Command has been handled by mca_char.
* Start clean with a prompt.
* Not a multi-char command
* (at least, not anymore).
* Decode the command character and decide what to do.
* 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)
* 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.
action
= cmd_decode(cbuf
, &s
);
* If an "extra" string was returned,
* process it as a string of command characters.
* Clear the cmdbuf string.
* (But not if we're in the prefix of a command,
* because the partial command string is kept there.)
* First digit of a number.
* Forward one window (and set the window size).
* Backward one window (and set the window size).
* Forward N (default 1) line.
* Backward N (default 1) line.
* Force forward N (default 1) line.
* Force backward N (default 1) line.
* Forward forever, ignoring EOF.
* (default same as last 'd' or 'u' command).
* (default same as last 'd' or 'u' command).
* Flush buffers, then repaint screen.
* Don't flush the buffers on a pipe!
* Go to line N, default beginning of file.
* Go to a specified percentage into the file.
* Go to line N, default end of file.
* Go to a specified byte position in the file.
jump_line_loc((POSITION
)number
, jump_sline
);
parg
.p_string
= eq_message();
* Print version number, without the "@(#)".
parg
.p_string
= version
+4;
* Define abbreviation for a commonly used sequence below.
#define DO_SEARCH() if (number <= 0) number = 1; \
multi_search((char *)NULL, number);
* Search forward for a pattern.
* Get the first char of the pattern.
* Search backward for a pattern.
* Get the first char of the pattern.
* Repeat previous search.
* Repeat previous search, multiple files.
search_type
|= SRCH_PAST_EOF
;
* Repeat previous search, in reverse direction.
save_search_type
= search_type
;
search_type
= SRCH_REVERSE(search_type
);
search_type
= save_search_type
;
* Repeat previous search,
* multiple files in reverse direction.
save_search_type
= search_type
;
search_type
= SRCH_REVERSE(search_type
);
search_type
|= SRCH_PAST_EOF
;
search_type
= save_search_type
;
* Edit a new file. Get the filename.
start_mca(A_EXAMINE
, "Examine: ");
* Invoke an editor on the input file.
if (strcmp(get_filename(curr_ifile
), "-") == 0)
error("Cannot edit standard input", NULL_PARG
);
* Expand the editor prototype string
* and pass it to the system to execute.
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);
error("Command not available", NULL_PARG
);
if (quit_at_eof
&& hit_eof
)
parg
.p_string
= (number
> 1) ? "(N-th) " : "";
error("No %snext file", &parg
);
parg
.p_string
= (number
> 1) ? "(N-th) " : "";
error("No %sprevious file", &parg
);
* Examine a particular file.
error("No such file", NULL_PARG
);
start_mca(A_OPT_TOGGLE
, "-");
start_mca(A_DISP_OPTION
, "_");
if (c
== erase_char
|| c
== kill_char
)
toggle_option(c
, "", OPT_NO_TOGGLE
);
* Set an initial command for new files.
start_mca(A_FIRSTCMD
, "+");
error("Command not available", NULL_PARG
);
start_mca(A_SETMARK
, "mark: ");
if (c
== erase_char
|| c
== kill_char
||
start_mca(A_GOMARK
, "goto mark: ");
if (c
== erase_char
|| c
== kill_char
||
start_mca(A_PIPE
, "|mark: ");
if (c
== erase_char
|| c
== kill_char
)
if (c
== '\n' || c
== '\r')
start_mca(action
, "Brackets: ");
* The command is incomplete (more chars are needed).
* Display the current char, so the user knows
* what's going on, and get another character.
start_mca(A_PREFIX
, " ");