From ef4604d9c146dbab1a653f66209103b74e6feb36 Mon Sep 17 00:00:00 2001 From: scratko Date: Thu, 20 Jun 2024 18:38:11 +0300 Subject: Shell-edit release Autocomplete program and file names (tab). Moving the cursor to edit commands (left and right arrows). Deleting a character in a command (backspace). --- shell.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 150 insertions(+), 23 deletions(-) (limited to 'shell.c') diff --git a/shell.c b/shell.c index 71be7fd..2944f00 100644 --- a/shell.c +++ b/shell.c @@ -2,6 +2,8 @@ #include "queue.h" #include "dynamic_array.h" #include "lexical_analysis.h" +#include "file_suggestions.h" +#include "readline.h" #include #include @@ -11,6 +13,7 @@ #include #include #include +#include MAKE_QUEUE_INIT(w_queue) MAKE_QUEUE_INIT(p_queue) @@ -23,6 +26,7 @@ enum modes { word_separation, whole_word }; static void show_invitation() { printf("> "); + fflush(stdout); } /* remove background zombie processes */ @@ -54,6 +58,7 @@ static void init_params(struct param_type *params, enum modes *current_mode) /* 0 - success, 1 - error */ params->last_execution_status = 0; params->pipeline = 0; + params->new_readline = 1; *current_mode = word_separation; } @@ -74,11 +79,13 @@ static void reset_params(struct param_type *params, enum modes *current_mode, struct w_queue *word_chain, struct c_queue *cmdlines, - struct dynamic_array *tmp_word) + struct dynamic_array *tmp_word, + struct readline_type *readline) { w_queue_clear(word_chain); c_queue_clear(cmdlines); - dynarr_drop_word(tmp_word); + dynarr_reset_array(tmp_word); + readline_reset_array(readline); clear_filename(params); init_params(params, current_mode); } @@ -126,11 +133,13 @@ void error_identification(const struct param_type *params) static int special_token_handling(struct w_queue *word_chain, struct c_queue *cmdlines, struct dynamic_array *tmp_word, int ch, - struct param_type *params) + struct param_type *params, + struct readline_type *readline) { return - stream_redirect_tokens(word_chain, tmp_word, ch, params) ? - 1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params); + stream_redirect_tokens(word_chain, tmp_word, ch, params, readline) ? + 1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params, + readline); } static void wait_for_process_to_complete(struct p_queue *pid_store) @@ -394,7 +403,7 @@ void add_filename(struct dynamic_array *tmp_word, struct param_type *params) malloc(tmp_word->last_element_index+1); dynarr_copy_array(tmp_word, params->streams.output_stream_to_append); } - dynarr_drop_word(tmp_word); + dynarr_reset_array(tmp_word); params->is_word = 0; } @@ -405,7 +414,7 @@ void add_word(struct w_queue *word_chain, struct dynamic_array *tmp_word, char *word = malloc(tmp_word->last_element_index+1); dynarr_copy_array(tmp_word, word); w_queue_push(word_chain, word); - dynarr_drop_word(tmp_word); + dynarr_reset_array(tmp_word); params->is_word = 0; } @@ -416,13 +425,86 @@ char** create_cmdline(const struct w_queue *word_chain, int word_counter) return cmdline; } +static int readline_termination_condition(int ch, + struct readline_type *readline) +{ + return + ch == new_line || + (ch == end_of_file && readline->last_element_index == -1); +} + +static void echo_characters(int ch) +{ + write(1, &ch, 1); +} + +static void generate_readline(struct readline_type *readline) +{ + enum keys found_key = none; + int match_result; + char ch; + + while((ch = getchar()) && !readline_termination_condition(ch, readline)) { + if(ch == tab) { + if(suggestions_for_filename(readline)) { + match_result = get_filename_match(readline, + get_start_filename_idx(readline)); + if(!match_result) + echo_characters(new_line); + show_invitation(); + if(readline->last_element_index != -1) + readline_print(readline); + /* program name is not empty */ + } else if(readline->last_element_index != -1) { + match_result = get_program_name_match(readline); + if(!match_result) + echo_characters(new_line); + show_invitation(); + if(readline->last_element_index != -1) + readline_print(readline); + } + continue; + } + if(ch == end_of_file) + continue; + + /* moving */ + found_key = readline_detect_arrow_keys(ch); + if(found_key != none && found_key != unknown_key) { + readline_move_along(readline, found_key); + continue; + } + if(found_key == unknown_key) + continue; + + /* deletion */ + if(ch == backspace) { + readline_character_deletion(readline); + continue; + } + + echo_characters(ch); + readline_add_char(readline, ch); + } + /* moving cursor to end */ + if(ch == new_line) + readline->cursor_pos = readline->last_element_index + 1; + /* adding '\n' to readline */ + readline_add_char(readline, ch); + /* print '\n' to end of readline */ + if(ch == new_line) + echo_characters(ch); +} + static void command_processing(struct param_type *params, - enum modes *current_mode, - struct w_queue *word_chain, - struct c_queue *cmdlines, - struct dynamic_array *tmp_word, int ch) + enum modes *current_mode, + struct w_queue *word_chain, + struct c_queue *cmdlines, + struct dynamic_array *tmp_word, + struct readline_type *readline, int ch) { char **cmdline = NULL; + int last_token = 0; /* odd number of double quotes */ if(!is_double_quotes_pair(*params)) { @@ -463,16 +545,22 @@ static void command_processing(struct param_type *params, clean: error_identification(params); - if(params->tokens == '&' || params->tokens == 0 || ch == new_line) + last_token = params->tokens; + reset_params(params, current_mode, word_chain, cmdlines, tmp_word, + readline); + + if(last_token == '&' || last_token == 0 || ch == new_line) { show_invitation(); - reset_params(params, current_mode, word_chain, cmdlines, tmp_word); + generate_readline(readline); + } } static void word_separation_processing(int ch, struct param_type *params, enum modes *current_mode, struct w_queue *word_chain, struct c_queue *cmdlines, - struct dynamic_array *tmp_word) + struct dynamic_array *tmp_word, + struct readline_type *readline) { /* could be a marker for the beginning of a blank word */ params->stored_symbol = ch; @@ -492,16 +580,18 @@ static void word_separation_processing(int ch, struct param_type *params, params->escape_sequences = 1; return; } - if(special_token_handling(word_chain, cmdlines, tmp_word, ch, params)) { + if(special_token_handling(word_chain, cmdlines, tmp_word, ch, params, + readline)) { if(command_execution_condition(params)) command_processing(params, current_mode, word_chain, cmdlines, - tmp_word, ch); + tmp_word, readline, ch); return; } if(params->wrong_command) { error_identification(params); clean_input_buffer(); - reset_params(params, current_mode, word_chain, cmdlines, tmp_word); + reset_params(params, current_mode, word_chain, cmdlines, tmp_word, + readline); show_invitation(); return; } @@ -509,7 +599,8 @@ static void word_separation_processing(int ch, struct param_type *params, if(excessive_words(ch, params)) { error_identification(params); clean_input_buffer(); - reset_params(params, current_mode, word_chain, cmdlines, tmp_word); + reset_params(params, current_mode, word_chain, cmdlines, tmp_word, + readline); show_invitation(); return; } @@ -560,33 +651,69 @@ static void whole_word_processing(int ch, struct param_type *params, add_letter(ch, tmp_word, params); } +static void change_terminal_settings(struct termios *cur_terminal_settings, + struct termios *save_terminal_settings) +{ + tcgetattr(0, save_terminal_settings); + memcpy(cur_terminal_settings, save_terminal_settings, + sizeof(*save_terminal_settings)); + cur_terminal_settings->c_lflag &= ~(ICANON | ECHO); + tcsetattr(0, TCSANOW, cur_terminal_settings); +} + +static void restore_terminal_settings(struct termios *cur_terminal_settings, + struct termios *save_terminal_settings) +{ + tcsetattr(0, TCSANOW, save_terminal_settings); +} + int main() { - int ch; + char ch; + int i; struct param_type params; + struct termios cur_terminal_settings, save_terminal_settings; struct w_queue word_chain; struct c_queue cmdlines; struct dynamic_array tmp_word; + struct readline_type readline; enum modes current_mode = word_separation; + change_terminal_settings(&cur_terminal_settings, &save_terminal_settings); w_queue_init(&word_chain); c_queue_init(&cmdlines); dynarr_create_array(&tmp_word); + readline_create_array(&readline); init_params(¶ms, ¤t_mode); show_invitation(); + generate_readline(&readline); + + for(i = 0; readline.arr[i] != end_of_file; ) { + ch = readline.arr[i]; + readline.considered_index = i; + + if(params.new_readline) + params.new_readline = 0; - while((ch = getchar()) != EOF) { if(ch == new_line) command_processing(¶ms, ¤t_mode, &word_chain, - &cmdlines, &tmp_word, ch); - else if(current_mode == word_separation) + &cmdlines, &tmp_word, &readline, ch); + else if(current_mode == word_separation) { word_separation_processing(ch, ¶ms, ¤t_mode, &word_chain, - &cmdlines, &tmp_word); + &cmdlines, &tmp_word, &readline); + /* double token was found */ + if(readline.considered_index != i) + ++i; + } else if(current_mode == whole_word) whole_word_processing(ch, ¶ms, ¤t_mode, &word_chain, &tmp_word); + i = params.new_readline ? 0 : i + 1; } + + restore_terminal_settings(&cur_terminal_settings, &save_terminal_settings); putchar(new_line); dynarr_clear(&tmp_word); + readline_clear(&readline); return 0; } -- cgit v1.2.3