From c9d02770f42339b6cc741191cee87e77b914b00b Mon Sep 17 00:00:00 2001 From: scratko Date: Mon, 3 Jun 2024 14:40:07 +0300 Subject: Shell-IV release Redirecting standard input-output streams. --- shell.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 300 insertions(+), 56 deletions(-) (limited to 'shell.c') diff --git a/shell.c b/shell.c index c03b62b..9ce5a32 100644 --- a/shell.c +++ b/shell.c @@ -1,10 +1,12 @@ #include "queue.h" #include "dynamic_array.h" + #include #include #include #include #include +#include enum modes { word_separation, whole_word }; enum { @@ -22,6 +24,13 @@ enum { or = '|' + 1 }; +/* 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; @@ -30,6 +39,8 @@ struct param_type { int empty_word_flag; int tokens; int wrong_command; + struct io_type streams; + int last_execution_status; }; static void show_invitation() @@ -51,17 +62,36 @@ static void init_params(struct param_type *params, enum modes *current_mode) params->empty_word_flag = 0; params->tokens = 0; params->wrong_command = 0; + params->streams.input_stream = NULL; + params->streams.output_stream = NULL; + params->streams.output_stream_to_append = NULL; + /* 0 - success, 1 - error */ + params->last_execution_status = 0; *current_mode = word_separation; } +static void clear_filename(struct param_type *params) +{ + if(params->streams.input_stream) + free(params->streams.input_stream); + if(params->streams.output_stream) + free(params->streams.output_stream); + if(params->streams.output_stream_to_append) + free(params->streams.output_stream_to_append); + params->streams.input_stream = NULL; + params->streams.output_stream = NULL; + params->streams.output_stream_to_append = NULL; +} + static void reset_params(struct param_type *params, - enum modes *current_mode, - struct queue *word_chain, - struct dynamic_array *tmp_word) + enum modes *current_mode, + struct queue *word_chain, + struct dynamic_array *tmp_word) { - init_params(params, current_mode); queue_clear(word_chain); dynarr_drop_word(tmp_word); + clear_filename(params); + init_params(params, current_mode); } static int is_double_quotes_pair(struct param_type params) @@ -75,48 +105,173 @@ static int check_separation(int ch, struct param_type params) !params.escape_sequences; } -static int single_tokens(int ch) +static int is_stream_redirection_set(const struct param_type *params) +{ + return params->streams.input_stream || params->streams.output_stream || + params->streams.output_stream_to_append; +} + +static int filename_waiting(const struct param_type *params) +{ + return params->tokens == '<' || params->tokens == '>' || + params->tokens == append; +} + +static void add_filename(struct dynamic_array *tmp_word, + struct param_type *params) +{ + dynarr_push_back(tmp_word, '\0'); + switch(params->tokens) { + case '<': + params->streams.input_stream = malloc(tmp_word->last_element_index+1); + dynarr_copy_array(tmp_word, params->streams.input_stream); + break; + case '>': + params->streams.output_stream = malloc(tmp_word->last_element_index+1); + dynarr_copy_array(tmp_word, params->streams.output_stream); + break; + case append: + params->streams.output_stream_to_append = + malloc(tmp_word->last_element_index+1); + dynarr_copy_array(tmp_word, params->streams.output_stream_to_append); + } + dynarr_drop_word(tmp_word); + params->is_word = 0; +} + +static void add_word(struct queue *word_chain, + struct dynamic_array *tmp_word, + struct param_type *params) +{ + dynarr_push_back(tmp_word, 0); + char *word = malloc(tmp_word->last_element_index+1); + dynarr_copy_array(tmp_word, word); + queue_push(word_chain, word); + dynarr_drop_word(tmp_word); + params->is_word = 0; +} + +static int is_double_token(struct param_type *params) +{ + return params->tokens == and || params->tokens == or || + params->tokens == append; +} + +static int is_redirect_token(int ch, int next_ch) +{ + return ch == '<' || ch == '>' || (ch == '>' && next_ch == '>'); +} + +static int validate_redirections(int ch, int next_ch, struct param_type *params) { - return ch == '<' || ch == ';' || ch == '(' || ch == ')'; + return (ch == '<' && params->streams.input_stream == NULL) || + ((ch == '>' || (ch == '>' && next_ch == '>')) && + params->streams.output_stream == NULL && + params->streams.output_stream_to_append == NULL); +} + +static int stream_redirect_tokens(struct queue *word_chain, + struct dynamic_array *tmp_word, int ch, + struct param_type *params) +{ + int next_ch; + next_ch = getchar(); + ungetc(next_ch, stdin); + + if(is_redirect_token(ch, next_ch)) { + /* filenames */ + if(filename_waiting(params)) + if(params->is_word) + add_filename(tmp_word, params); + else { + fprintf(stderr, "syntax error\n"); + params->wrong_command = 1; + return 0; + } + /* execute command */ + else if(params->is_word) { + add_word(word_chain, tmp_word, params); + params->is_word = 0; + } + + if(validate_redirections(ch, next_ch, params)) { + params->tokens = (ch == '>' && next_ch == '>') ? append : ch; + if(is_double_token(params)) + getchar(); + return 1; + } else { + fprintf(stderr, "syntax error\n"); + params->wrong_command = 1; + } + } + return 0; } static void clean_input_buffer() { int ch; - while((ch = getchar() != new_line)) + while((ch = getchar()) != new_line) {} } -static void double_tokens_and_fg(int ch, struct param_type *params) +static int is_special_token(int ch) +{ + return ch == and || ch == or || ch == '&' || ch == ';' || ch == '|'; +} + +static int special_tokens_allowed(int ch, struct param_type *params) +{ + return (!filename_waiting(params) || + (filename_waiting(params) && params->is_word)); +} + +static int special_tokens(struct dynamic_array *tmp_word, int ch, + struct param_type *params) { int next_ch; next_ch = getchar(); ungetc(next_ch, stdin); - if(ch == '>' && next_ch == '>') - params->tokens = append; - else if(ch == '|' && next_ch == '|') - params->tokens = or; - else if(ch == '&' && next_ch == '&') - params->tokens = and; - else if(ch == '&') { - while((ch = getchar()) != new_line) { - if(ch != whitespace && ch != tab) { - fprintf(stderr, "incorrect command\n"); - params->wrong_command = 1; - return; + + if(is_special_token(ch)) { + if(special_tokens_allowed(ch, params)) { + if(filename_waiting(params) && params->is_word) { + add_filename(tmp_word, params); + params->is_word = 0; + } + if(ch == '|') + params->tokens = '|'; + else if(ch == '|' && next_ch == '|') + params->tokens = or; + else if(ch == '&' && next_ch == '&') + params->tokens = and; + else if(ch == '&') { + while((ch = getchar()) != new_line) { + if(ch != whitespace && ch != tab) { + fprintf(stderr, "incorrect command\n"); + params->wrong_command = 1; + return 0; + } + } + params->tokens = '&'; } + if(is_double_token(params)) + getchar(); + return 1; + } else { + params->wrong_command = 1; + fprintf(stderr, "filename expected\n"); } - params->tokens = '&'; } + return 0; } -static int check_tokens(int ch, struct param_type *params) +static int special_token_handling(struct queue *word_chain, + struct dynamic_array *tmp_word, int ch, + struct param_type *params) { - if(single_tokens(ch)) - params->tokens = ch; - else - double_tokens_and_fg(ch, params); - return params->tokens; + return + stream_redirect_tokens(word_chain, tmp_word, ch, params) ? + 1 : special_tokens(tmp_word, ch, params); } static int ignore_spaces(int ch, struct param_type params) @@ -134,16 +289,35 @@ static int start_escape_sequence(int ch, struct param_type params) return ch == backslash && !params.escape_sequences; } -static void add_word(struct queue *word_chain, - struct dynamic_array *tmp_word, - struct param_type *params) +/* name of file for opening stream already exists */ + +static int excessive_words(int ch, const struct param_type *params) { - dynarr_push_back(tmp_word, 0); - char *word = malloc(tmp_word->last_element_index+1); - dynarr_copy_array(tmp_word, word); - queue_push(word_chain, word); - dynarr_drop_word(tmp_word); - params->is_word = 0; + int next_ch; +#if 0 + if(filename_waiting(params)) + return + (params->tokens == '<' && params->streams.input_stream != NULL) || + (params->tokens == '>' && params->streams.output_stream !=NULL) || + (params->tokens == append && + params->streams.output_stream_to_append != NULL); + else + return 0; +#endif + if(filename_waiting(params)) { + if(ch == new_line) + return 0; + while((next_ch = getchar()) != new_line) { + if(next_ch == ' ') + continue; + if(!is_special_token(next_ch) && next_ch != '<' && next_ch != '>') + return 1; + else + break; + } + ungetc(next_ch, stdin); + } + return 0; } #if 0 @@ -171,7 +345,7 @@ static char** create_cmdline(const struct queue *word_chain, return cmdline; } -static int check_cd(const char *arg) +static int is_cd_command(const char *arg) { return !strcmp(arg, "cd"); } @@ -200,10 +374,12 @@ static void change_directory(char **cmdline) perror(path); } -static void clean_up_memory(struct queue *word_chain, char **cmdline) +static void clean_up_memory(struct queue *word_chain, char **cmdline, + struct param_type *params) { queue_clear(word_chain); free(cmdline); + clear_filename(params); } static void clean_up_zombie_process() @@ -214,19 +390,60 @@ static void clean_up_zombie_process() } while(pid > 0); } +static void open_files(const struct param_type *params, int *input_fd, + int *output_fd) +{ + if(params->streams.input_stream) + *input_fd = open(params->streams.input_stream, O_RDONLY); + if(params->streams.output_stream) + *output_fd = open(params->streams.output_stream, O_WRONLY | O_CREAT | + O_TRUNC, 0666); + if(params->streams.output_stream_to_append) + *output_fd = open(params->streams.output_stream_to_append, O_WRONLY | + O_APPEND, 0666); +} + +static void change_streams(int input_fd, int output_fd) +{ + if(input_fd) { + dup2(input_fd, 0); + close(input_fd); + } + if(output_fd) { + dup2(output_fd, 1); + close(output_fd); + } +} + +static void close_files(int input_fd, int output_fd) +{ + if(input_fd) + close(input_fd); + if(output_fd) + close(output_fd); +} + static void run_external_program(struct queue *word_chain, struct param_type *params) { - int pid, wait_pid, result; + int pid, wait_pid, result, input_fd, output_fd; if(word_chain->first == NULL) { fprintf(stderr, "empty command\n"); return; } + + if(is_stream_redirection_set(params)) + open_files(params, &input_fd, &output_fd); + + if(input_fd == -1 || output_fd == -1) { + perror("can't open file"); + return; + } char **cmdline = create_cmdline(word_chain, queue_get_word_count(word_chain)); - if(check_cd(cmdline[0])) { + if(is_cd_command(cmdline[0])) { change_directory(cmdline); - clean_up_memory(word_chain, cmdline); + clean_up_memory(word_chain, cmdline, params); } else { pid = fork(); @@ -236,13 +453,17 @@ static void run_external_program(struct queue *word_chain, } /* child process */ if(pid == 0) { + if(is_stream_redirection_set(params)) + change_streams(input_fd, output_fd); execvp(cmdline[0], cmdline); + params->last_execution_status = 1; perror(cmdline[0]); exit(1); } /* parent process */ - clean_up_memory(word_chain, cmdline); + close_files(input_fd, output_fd); + clean_up_memory(word_chain, cmdline, params); /* waiting for forground process */ if(params->tokens != '&') { @@ -257,7 +478,7 @@ static void run_external_program(struct queue *word_chain, static void command_processing(struct param_type *params, enum modes *current_mode, struct queue *word_chain, - struct dynamic_array *tmp_word) + struct dynamic_array *tmp_word, int ch) { /* and odd number of double quotes */ if(!is_double_quotes_pair(*params)) { @@ -266,18 +487,31 @@ static void command_processing(struct param_type *params, show_invitation(); return; } + if(params->empty_word_flag || params->is_word) { - if(params->empty_word_flag) - dynarr_push_back(tmp_word, '\0'); - add_word(word_chain, tmp_word, params); + if(filename_waiting(params)) + if(excessive_words(ch, params)) { + fprintf(stderr, "too many args\n"); + goto clean; + } else + add_filename(tmp_word, params); + else + add_word(word_chain, tmp_word, params); + } else if(filename_waiting(params)) { + fprintf(stderr, "filename expected\n"); + goto clean; } - run_external_program(word_chain, params); - if(params->tokens == '&' || params->tokens == 0) + + if((params->tokens == and || params->tokens == or) && + params->last_execution_status == 1) + fprintf(stderr, "cannot be performed\n"); + else + run_external_program(word_chain, params); + +clean: + if(params->tokens == '&' || params->tokens == 0 || ch == new_line) show_invitation(); reset_params(params, current_mode, word_chain, tmp_word); -#if 0 - print_line(word_chain); -#endif } static int is_empty_word(int ch, struct param_type params) @@ -309,8 +543,9 @@ static void word_separation_processing(int ch, struct param_type *params, params->escape_sequences = 1; return; } - if(check_tokens(ch, params)) { - command_processing(params, current_mode, word_chain, tmp_word); + if(special_token_handling(word_chain, tmp_word, ch, params)) { + if(!filename_waiting(params)) + command_processing(params, current_mode, word_chain, tmp_word, ch); return; } if(params->wrong_command) { @@ -320,8 +555,17 @@ static void word_separation_processing(int ch, struct param_type *params, return; } if(check_separation(ch, *params)) { - add_word(word_chain, tmp_word, params); - params->is_word = 0; + if(excessive_words(ch, params)) { + clean_input_buffer(); + reset_params(params, current_mode, word_chain, tmp_word); + fprintf(stderr, "too many args\n"); + show_invitation(); + return; + } + if(!filename_waiting(params)) { + add_word(word_chain, tmp_word, params); + params->is_word = 0; + } return; } if(ignore_spaces(ch, *params)) @@ -395,7 +639,7 @@ int main() clean_up_zombie_process(); if(ch == new_line) command_processing(¶ms, ¤t_mode, &word_chain, - &tmp_word); + &tmp_word, ch); else if(current_mode == word_separation) word_separation_processing(ch, ¶ms, ¤t_mode, &word_chain, &tmp_word); -- cgit v1.2.3