From 74c6a747a58b38131534556ab95fa4dd4b514780 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). --- Makefile | 6 +- dynamic_array.c | 3 +- dynamic_array.h | 2 +- file_suggestions.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++ file_suggestions.h | 12 +++ lexical_analysis.c | 28 +++--- lexical_analysis.h | 6 +- readline.c | 153 +++++++++++++++++++++++++++++++ readline.h | 37 ++++++++ shell.c | 173 ++++++++++++++++++++++++++++++----- shell.h | 64 +++++++++++++ 11 files changed, 700 insertions(+), 42 deletions(-) create mode 100644 file_suggestions.c create mode 100644 file_suggestions.h create mode 100644 readline.c create mode 100644 readline.h create mode 100644 shell.h diff --git a/Makefile b/Makefile index 8a49831..2bbac5e 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ -SRCMODULES = dynamic_array.c queue.c lexical_analysis.c shell.c +SRCMODULES = dynamic_array.c queue.c lexical_analysis.c file_suggestions.c readline.c shell.c OBJMODULES = $(SRCMODULES:.c=.o) CC = gcc CFLAGS = -Wall -g -c -all: shell-V +all: shell-edit %.o: %.с %.h $(CC) $(CFLAGS) $< -o $@ -shell-V: $(OBJMODULES) +shell-edit: $(OBJMODULES) $(CC) $(LIBS) $^ -o $@ -include deps.mk diff --git a/dynamic_array.c b/dynamic_array.c index 5a24cb8..c0a8723 100644 --- a/dynamic_array.c +++ b/dynamic_array.c @@ -1,4 +1,5 @@ #include "dynamic_array.h" + #include void dynarr_create_array(struct dynamic_array *array) @@ -31,7 +32,7 @@ void dynarr_copy_array(struct dynamic_array *array, char *new_arr) new_arr[i] = array->arr[i]; } -void dynarr_drop_word(struct dynamic_array *array) +void dynarr_reset_array(struct dynamic_array *array) { array->last_element_index = -1; } diff --git a/dynamic_array.h b/dynamic_array.h index 98491ea..ae63fc4 100644 --- a/dynamic_array.h +++ b/dynamic_array.h @@ -14,7 +14,7 @@ struct dynamic_array { void dynarr_create_array(struct dynamic_array *array); void dynarr_push_back(struct dynamic_array *array, int letter); void dynarr_copy_array(struct dynamic_array *array, char *new_arr); -void dynarr_drop_word(struct dynamic_array *array); +void dynarr_reset_array(struct dynamic_array *array); void dynarr_clear(struct dynamic_array *array); #endif diff --git a/file_suggestions.c b/file_suggestions.c new file mode 100644 index 0000000..528d717 --- /dev/null +++ b/file_suggestions.c @@ -0,0 +1,258 @@ +#include "file_suggestions.h" +#include "readline.h" + +#include +#include +#include +#include +#include + +extern char **environ; + +static void clean_name_and_path(char *name, char *path) +{ + if(name) + free(name); + if(strcmp(path, ".")) + free(path); +} + +static int print_all_filenames(const char *name, const char *path) +{ + DIR *dir; + struct dirent *dent; + char *next_filename; + int first_suggestion; + + first_suggestion = 1; + + dir = opendir(path); + if(!dir) { + perror(path); + return 0; + } + while((dent = readdir(dir)) != NULL) { + next_filename = dent->d_name; + if(first_suggestion) { + putchar('\n'); + first_suggestion = 0; + } + printf("%s\n", next_filename); + } + closedir(dir); + return 1; +} + +static int matchmaking(const char *name, const char *path) +{ + DIR *dir; + struct dirent *dent; + char *next_filename; + int mismatch_status, first_suggestion, something_found; + + mismatch_status = 0; + something_found = 0; + first_suggestion = 1; + + dir = opendir(path); + if(!dir) { + perror(path); + return 0; + } + while((dent = readdir(dir)) != NULL) { + next_filename = dent->d_name; + int i; + for(i = 0; name[i] && next_filename[i]; ++i) { + if(name[i] != next_filename[i]) { + mismatch_status = 1; + break; + } + } + if(!mismatch_status && name[i] == '\0') { + if(first_suggestion) { + putchar('\n'); + first_suggestion = 0; + } + printf("%s\n", next_filename); + if(!something_found) + something_found = 1; + } + mismatch_status = 0; + } + closedir(dir); + return something_found; +} + +static void extract_path_and_name(char **path, char **name, + struct readline_type *readline, + int start_filename_idx) +{ + int i, j, k; + + for(i = readline->last_element_index; i >= start_filename_idx; --i) { + if(readline->arr[i] == '/') + break; + } + *path = malloc(i - start_filename_idx+ 2); + *name = malloc(readline->last_element_index - i + 1); + + /* extract path */ + for(j = start_filename_idx, k = 0; j <= i; ++j, ++k) + (*path)[k] = readline->arr[j]; + (*path)[k] = '\0'; + + /* extract name */ + for(j = i + 1, k = 0; j <= readline->last_element_index; ++j, ++k) + (*name)[k] = readline->arr[j]; + (*name)[k] = '\0'; +} + +static void copy_filename(char *dest, char *source, int start_idx, int last_idx) +{ + int i, j; + for(i = 0, j = start_idx; j <= last_idx; ++i, ++j) + dest[i] = source[j]; +} + +static void extract_only_filename(char **name, struct readline_type *readline, + int start_filename_idx) +{ + int size = readline->last_element_index - start_filename_idx + 2; + *name = malloc(size); + copy_filename(*name, readline->arr, start_filename_idx, + readline->last_element_index); + (*name)[size-1] = '\0'; +} + +int get_filename_match(struct readline_type *readline, int start_filename_idx) +{ + int extract_path_status, match_result; + + char *name = NULL; + extract_path_status = 0; + match_result = 0; + /* default path */ + char *path = "."; + int i; + + for(i = start_filename_idx; i <= readline->last_element_index; ++i) { + if(readline->arr[i] == '/') { + extract_path_status = 1; + break; + } + } + /* not empty filename */ + if(readline->arr[start_filename_idx] != ' ') + extract_path_status ? extract_path_and_name(&path, &name, readline, + start_filename_idx) : + extract_only_filename(&name, readline, + start_filename_idx); + match_result = + (name == NULL && !strcmp(path, ".")) ? print_all_filenames(name, path) : + matchmaking(name, path); + clean_name_and_path(name, path); + return match_result; +} + +static int is_token(int ch) +{ + return ch == ' ' || ch == '|' || ch == '&' || ch == ';' || ch == '<' || + ch == '>'; +} + +int get_start_filename_idx(struct readline_type *readline) +{ + /* empty filename */ + if(readline->last_element_index == ' ') + return readline->last_element_index; + + int i; + for(i = readline->last_element_index; i != -1; --i) { + if(is_token(readline->arr[i])) + return ++i; + } + return 0; +} + +/* + * 1 searching for suggestions for file name, + * 0 searching for program name from PATH + */ +int suggestions_for_filename(struct readline_type *readline) +{ + int token_status, i; + token_status = 0; + + for(i = readline->last_element_index; i != -1; --i) { + if(is_token(readline->arr[i])) { + token_status = 1; + continue; + } + if(token_status && !is_token(readline->arr[i])) + return 1; + } + return 0; +} + +int find_separator_index(char *path, int begin_idx) +{ + int i; + for(i = begin_idx; path[i] != ':' && path[i]; ++i) + {} + return i; +} + +int get_program_name_match(struct readline_type *readline) +{ + char *name = NULL; + char *path = NULL; + char *path_variable = NULL; + int size, begin_idx, end_idx, result; + + begin_idx = 0; + end_idx = 0; + result = 0; + size = readline->last_element_index + 2; + name = malloc(size); + + /* extracting prog name */ + int i; + for(i = 0; i <= readline->last_element_index; ++i) + name[i] = readline->arr[i]; + name[i] = '\0'; + + /* searching PATH */ + for(i = 0; environ[i]; ++i) + if(!strncmp(environ[i], "PATH", 4)) { + path_variable = environ[i]; + break; + } + if(!path_variable) + return 0; + + /* skipping PATH= */ + path_variable = path_variable + 5; + + /* directory search by separator (:) */ + end_idx = find_separator_index(path_variable, begin_idx); + size = end_idx + 1; + path = malloc(size); + copy_filename(path, path_variable, begin_idx, end_idx - 1); + path[end_idx] = '\0'; + result = matchmaking(name, path) ? 1 : result; + free(path); + + /* if end_idx is beginning with ':' */ + while(path_variable[end_idx]) { + begin_idx = end_idx + 1; + end_idx = find_separator_index(path_variable, begin_idx); + size = end_idx + 1; + path = malloc(size); + copy_filename(path, path_variable, begin_idx, end_idx - 1); + path[end_idx] = '\0'; + result = matchmaking(name, path) ? 1 : result; + free(path); + } + free(name); + return result; +} diff --git a/file_suggestions.h b/file_suggestions.h new file mode 100644 index 0000000..1b818a8 --- /dev/null +++ b/file_suggestions.h @@ -0,0 +1,12 @@ +#ifndef FILE_SUGGESTIONS_H_SENTRY +#define FILE_SUGGESTIONS_H_SENTRY + +#include "readline.h" + +int get_filename_match(struct readline_type *readline, int start_filename_idx); +int get_program_name_match(struct readline_type *readline); + +int get_start_filename_idx(struct readline_type *readline); +int suggestions_for_filename(struct readline_type *readline); + +#endif diff --git a/lexical_analysis.c b/lexical_analysis.c index 4349a69..5db57cb 100644 --- a/lexical_analysis.c +++ b/lexical_analysis.c @@ -123,11 +123,13 @@ int is_redirect_token(int ch, int next_ch) */ int stream_redirect_tokens(struct w_queue *word_chain, struct dynamic_array *tmp_word, int ch, - struct param_type *params) + struct param_type *params, + struct readline_type *readline) { int next_ch; - next_ch = getchar(); - ungetc(next_ch, stdin); + + if(ch == '>') + next_ch = readline->arr[readline->considered_index + 1]; if(is_redirect_token(ch, next_ch)) { add_word_or_filename(word_chain, tmp_word, params); @@ -138,7 +140,7 @@ int stream_redirect_tokens(struct w_queue *word_chain, if(validate_redirections(ch, next_ch, params)) { params->tokens = (ch == '>' && next_ch == '>') ? append : ch; if(is_double_token(params)) - getchar(); + ++readline->considered_index; return 1; } else params->wrong_command = err_redirect_stream_again; @@ -178,7 +180,7 @@ int pipeline_token_processing(struct w_queue *word_chain, c_queue_push(cmdlines, cmdline); w_queue_clear(word_chain); - dynarr_drop_word(tmp_word); + dynarr_reset_array(tmp_word); return 1; } @@ -187,11 +189,12 @@ int pipeline_token_processing(struct w_queue *word_chain, */ int special_tokens(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) { - int next_ch; - next_ch = getchar(); - ungetc(next_ch, stdin); + int next_ch, i; + + if(ch == '|' || ch == '&') + next_ch = readline->arr[readline->considered_index + 1]; if(is_special_token(ch, next_ch)) { add_word_or_filename(word_chain, tmp_word, params); @@ -208,8 +211,9 @@ int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines, } else if(ch == '&' && next_ch == '&') params->tokens = and; else if(ch == '&') { - while((ch = getchar()) != new_line) { - if(ch != whitespace && ch != tab) { + for(i = readline->considered_index+1; readline->arr[i] != new_line; + ++i) { + if(readline->arr[i] != whitespace && readline->arr[i] != tab) { params->wrong_command = err_bg_process; return 0; } @@ -217,7 +221,7 @@ int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines, params->tokens = '&'; } if(is_double_token(params)) - getchar(); + ++readline->considered_index; return 1; } return 0; diff --git a/lexical_analysis.h b/lexical_analysis.h index 3266ad7..9d7d556 100644 --- a/lexical_analysis.h +++ b/lexical_analysis.h @@ -2,6 +2,7 @@ #define LEXICAL_ANALYSIS_H_SENTRY #include "shell.h" +#include "readline.h" int is_double_quotes_pair(struct param_type params); int escape_double_quotes_or_backslash(int ch, struct param_type params); @@ -26,7 +27,8 @@ int is_double_token(struct param_type *params); int stream_redirect_tokens(struct w_queue *word_chain, struct dynamic_array *tmp_word, - int ch, struct param_type *params); + int ch, struct param_type *params, + struct readline_type *readline); int double_quotes_again(int ch, struct param_type params); @@ -37,6 +39,6 @@ int pipeline_token_processing(struct w_queue *word_chain, int special_tokens(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); #endif diff --git a/readline.c b/readline.c new file mode 100644 index 0000000..c36b19a --- /dev/null +++ b/readline.c @@ -0,0 +1,153 @@ +#include "readline.h" +#include "shell.h" + +#include +#include /* memmove */ +#include + +void readline_create_array(struct readline_type *readline) +{ + readline->arr = calloc(initial_size, 1); + readline->cursor_pos = 0; + readline->last_element_index = -1; + readline->considered_index = 0; + readline->allocation_size = initial_size; +} + +static void readline_copy_array(struct readline_type *readline, char *new_arr) +{ + int i; + for(i = 0; i <= readline->last_element_index; ++i) + new_arr[i] = readline->arr[i]; +} + +static void readline_allocate_memory(struct readline_type *readline) +{ + char *new_arr = calloc(readline->allocation_size * 2, 1); + readline_copy_array(readline, new_arr); + free(readline->arr); + readline->arr = new_arr; + readline->allocation_size *= 2; +} + +static void readline_reprint_modified_part(struct readline_type *readline, + int begin_idx) +{ + int i, counter; + for(i = begin_idx, counter = 0; i <= readline->last_element_index; + ++i, ++counter) + putchar(readline->arr[i]); + + /* return cursor to previous position */ + for( ; counter != 0; --counter) + putchar('\b'); + fflush(stdout); +} + +void readline_add_char(struct readline_type *readline, int ch) +{ + char *begin_arr; + int size; + + if(readline->last_element_index+1 == readline->allocation_size) + readline_allocate_memory(readline); + /* insert in middle position */ + if(readline->cursor_pos - 1 != readline->last_element_index) { + begin_arr = readline->arr + readline->cursor_pos; + size = readline->last_element_index - readline->cursor_pos + 1; + memmove(begin_arr+1, begin_arr, size); + readline->arr[readline->cursor_pos] = ch; + ++readline->last_element_index; + readline_reprint_modified_part(readline, readline->cursor_pos + 1); + } else { + ++readline->last_element_index; + readline->arr[readline->last_element_index] = ch; + } + ++readline->cursor_pos; +} + +void readline_reset_array(struct readline_type *readline) +{ + readline->last_element_index = -1; + readline->cursor_pos = 0; +} + +void readline_clear(struct readline_type *readline) +{ + free(readline->arr); + readline->arr = NULL; +} + +void readline_print(struct readline_type *readline) +{ + int i; + for(i = 0; i <= readline->last_element_index; ++i) + putchar(readline->arr[i]); + readline->cursor_pos = readline->last_element_index + 1; + fflush(stdout); +} + +enum keys readline_detect_arrow_keys(int ch) +{ + char next_ch; + if(ch == esc) { + next_ch = getchar(); + if(next_ch != '[') + return unknown_key; + next_ch = getchar(); + + switch(next_ch) { + case left_arrow: + return left_arrow; + case right_arrow: + return right_arrow; + case up_arrow: + return up_arrow; + case down_arrow: + return down_arrow; + default: + return unknown_key; + } + } + return none; +} + +void readline_move_along(struct readline_type *readline, enum keys found_key) +{ + switch(found_key) { + case left_arrow: + if(readline->cursor_pos > 0) { + putchar('\b'); + fflush(stdout); + --readline->cursor_pos; + } + break; + case right_arrow: + if(readline->cursor_pos <= readline->last_element_index) { + putchar(readline->arr[readline->cursor_pos]); + fflush(stdout); + ++readline->cursor_pos; + } + break; + default: + ; + } +} + +void readline_character_deletion(struct readline_type *readline) +{ + char *begin_arr; + int size; + + if(readline->cursor_pos > 0) { + putchar('\b'); + fflush(stdout); + begin_arr = readline->arr + readline->cursor_pos; + size = readline->last_element_index - readline->cursor_pos + 1; + memmove(begin_arr-1, begin_arr, size); + readline->arr[readline->last_element_index] = ' '; + readline_reprint_modified_part(readline, readline->cursor_pos - 1); + --readline->last_element_index; + --readline->cursor_pos; + } +} diff --git a/readline.h b/readline.h new file mode 100644 index 0000000..b626cae --- /dev/null +++ b/readline.h @@ -0,0 +1,37 @@ +#ifndef READLINE_H_SENTRY +#define READLINE_H_SENTRY + +#include "readline.h" + +/* + * ends with these codes in escape sequence + * for the arrows + */ +enum keys{ + left_arrow = 'D', + right_arrow = 'C', + up_arrow = 'A', + down_arrow = 'B', + none = 0, + unknown_key = -1, + backspace = 127 +}; + +struct readline_type { + char *arr; + int cursor_pos; + int considered_index; + int last_element_index; + int allocation_size; +}; + +void readline_create_array(struct readline_type *readline); +void readline_add_char(struct readline_type *readline, int ch); +void readline_reset_array(struct readline_type *readline); +void readline_clear(struct readline_type *readline); +void readline_print(struct readline_type *readline_type); +enum keys readline_detect_arrow_keys(int ch); +void readline_move_along(struct readline_type *readline, enum keys found_key); +void readline_character_deletion(struct readline_type *readline); + +#endif 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; } diff --git a/shell.h b/shell.h new file mode 100644 index 0000000..3fc11ad --- /dev/null +++ b/shell.h @@ -0,0 +1,64 @@ +#ifndef SHELL_H_SENTRY +#define SHELL_H_SENTRY + +#include "dynamic_array.h" +#include "queue.h" + +enum { + /* CTRL-D */ + end_of_file = 4, + new_line = 10, + whitespace = ' ', + tab = 9, + esc = 27, + backslash = '\\', + double_quotes = '"' +}; + +/* two-letter tokens */ +enum { + append = '>' + 1, + and = '&' + 1, + or = '|' + 1 +}; + +/* error codes */ +enum { + err_filename_expected = 1, + err_redirect_stream_again = 2, + err_redirect_stream_in_pipeline = 3, + err_bg_process = 4, + err_empty_command = 5, + err_extra_chars_after_filename = 6, + err_odd_double_quotes = 7 +}; + +/* storing file names to redirect standard input/output streams */ +struct io_type { + char *input_stream; + char *output_stream; + char *output_stream_to_append; +}; + +struct param_type { + int is_word; + int escape_sequences; + unsigned int double_quotes_counter; + char stored_symbol; + int empty_word_flag; + int tokens; + int wrong_command; + struct io_type streams; + int last_execution_status; + int pipeline; + int new_readline; +}; + +int filename_waiting(struct param_type *params); +void add_filename(struct dynamic_array *tmp_word, struct param_type *params); +void add_word(struct w_queue *word_chain, struct dynamic_array *tmp_word, + struct param_type *params); +int is_stream_redirection_set(const struct param_type *params); +char** create_cmdline(const struct w_queue *word_chain, int word_counter); + +#endif -- cgit v1.2.3