diff options
Diffstat (limited to 'shell.c')
-rw-r--r-- | shell.c | 495 |
1 files changed, 345 insertions, 150 deletions
@@ -1,5 +1,6 @@ #include "queue.h" #include "dynamic_array.h" +#include "lexical_analysis.h" #include <stdio.h> #include <stdlib.h> @@ -10,7 +11,13 @@ #include <signal.h> #include <errno.h> +MAKE_QUEUE_INIT(w_queue) +MAKE_QUEUE_INIT(p_queue) +MAKE_QUEUE_PUSH(w_queue, word, char*) +MAKE_QUEUE_PUSH(p_queue, pid, int) + enum modes { word_separation, whole_word }; +#if 0 enum { new_line = 10, whitespace = ' ', @@ -43,7 +50,9 @@ struct param_type { int wrong_command; struct io_type streams; int last_execution_status; + int pipeline; }; +#endif static void show_invitation() { @@ -83,6 +92,7 @@ static void init_params(struct param_type *params, enum modes *current_mode) params->streams.output_stream_to_append = NULL; /* 0 - success, 1 - error */ params->last_execution_status = 0; + params->pipeline = 0; *current_mode = word_separation; } @@ -101,40 +111,39 @@ static void clear_filename(struct param_type *params) static void reset_params(struct param_type *params, enum modes *current_mode, - struct queue *word_chain, + struct w_queue *word_chain, + struct c_queue *cmdlines, struct dynamic_array *tmp_word) { - queue_clear(word_chain); + w_queue_clear(word_chain); + c_queue_clear(cmdlines); dynarr_drop_word(tmp_word); clear_filename(params); init_params(params, current_mode); } - +#if 0 static int is_double_quotes_pair(struct param_type params) { return !(params.double_quotes_counter % 2); } - static int check_separation(int ch, struct param_type params) { return (ch == whitespace || ch == tab) && params.is_word && !params.escape_sequences; } - -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) +#endif +int filename_waiting(struct param_type *params) { - return params->tokens == '<' || params->tokens == '>' || - params->tokens == append; + if(params->tokens == '<' || params->tokens == '>' || + params->tokens == append) { + if(!params->is_word) + params->wrong_command = 1; + return 1; + } else + return 0; } -static void add_filename(struct dynamic_array *tmp_word, - struct param_type *params) +void add_filename(struct dynamic_array *tmp_word, struct param_type *params) { dynarr_push_back(tmp_word, '\0'); switch(params->tokens) { @@ -155,29 +164,35 @@ static void add_filename(struct dynamic_array *tmp_word, params->is_word = 0; } -static void add_word(struct queue *word_chain, - 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) { 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); + w_queue_push(word_chain, word); dynarr_drop_word(tmp_word); params->is_word = 0; } +static void add_letter(int ch, struct dynamic_array *tmp_word, + struct param_type *params) +{ + dynarr_push_back(tmp_word, ch); + params->is_word = 1; + params->escape_sequences = 0; +} + +#if 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 == '<' && params->streams.input_stream == NULL) || @@ -185,8 +200,19 @@ static int validate_redirections(int ch, int next_ch, struct param_type *params) params->streams.output_stream == NULL && params->streams.output_stream_to_append == NULL); } +static void add_word_or_filename(struct w_queue *word_chain, + struct dynamic_array *tmp_word, + struct param_type *params) +{ + /* filenames */ + if(filename_waiting(params) && !params->wrong_command) + add_filename(tmp_word, params); + /* execute command */ + else if(params->is_word) + add_word(word_chain, tmp_word, params); +} -static int stream_redirect_tokens(struct queue *word_chain, +static int stream_redirect_tokens(struct w_queue *word_chain, struct dynamic_array *tmp_word, int ch, struct param_type *params) { @@ -195,20 +221,10 @@ static int stream_redirect_tokens(struct queue *word_chain, 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; - } + add_word_or_filename(word_chain, tmp_word, params); + + if(params->wrong_command) + return 0; if(validate_redirections(ch, next_ch, params)) { params->tokens = (ch == '>' && next_ch == '>') ? append : ch; @@ -222,6 +238,7 @@ static int stream_redirect_tokens(struct queue *word_chain, } return 0; } +#endif static void clean_input_buffer() { @@ -229,67 +246,109 @@ static void clean_input_buffer() while((ch = getchar()) != new_line) {} } - +#if 0 static int is_special_token(int ch) { return ch == and || ch == or || ch == '&' || ch == ';' || ch == '|'; } +static int wrong_streams_redirection(struct param_type *params) +{ + return + (!params->pipeline && (params->tokens == '>' || + params->tokens == append)) || + (params->pipeline && (params->tokens == '>' || + params->tokens == append || + params->tokens == '<')); +} +#endif + +char** create_cmdline(const struct w_queue *word_chain, int word_counter) +{ + char **cmdline = malloc((word_counter + 1) * sizeof(char*)); + w_queue_copy_words_to_args(word_chain, cmdline); + return cmdline; +} -static int special_tokens_allowed(int ch, struct param_type *params) +int is_stream_redirection_set(const struct param_type *params) { - return (!filename_waiting(params) || - (filename_waiting(params) && params->is_word)); + return params->streams.input_stream || params->streams.output_stream || + params->streams.output_stream_to_append; +} +#if 0 +static int pipeline_token_processing(struct w_queue *word_chain, + struct c_queue *cmdlines, + struct dynamic_array *tmp_word, + struct param_type *params) +{ + char **cmdline = NULL; + if(is_stream_redirection_set(params) && + wrong_streams_redirection(params)) { + /* TODO: add error codes */ + params->wrong_command = 1; + return 0; + } + params->tokens = '|'; + params->pipeline = 1; + cmdline = + create_cmdline(word_chain, w_queue_get_word_count(word_chain)); + + c_queue_push(cmdlines, cmdline); + w_queue_clear(word_chain); + dynarr_drop_word(tmp_word); + return 1; } -static int special_tokens(struct dynamic_array *tmp_word, int ch, - struct param_type *params) +static int special_tokens(struct w_queue *word_chain, + struct c_queue *cmdlines, + struct dynamic_array *tmp_word, int ch, + struct param_type *params) { int next_ch; next_ch = getchar(); ungetc(next_ch, stdin); 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; - } + add_word_or_filename(word_chain, tmp_word, params); + + if(params->wrong_command) + return 0; + + if(ch == '|' && next_ch == '|') + params->tokens = or; + else if(ch == '|') { + if(!pipeline_token_processing(word_chain, cmdlines, tmp_word, + params)) + return 0; + } 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 = '&'; } + if(is_double_token(params)) + getchar(); + return 1; } return 0; } +#endif -static int special_token_handling(struct queue *word_chain, - struct dynamic_array *tmp_word, int ch, - 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) { return stream_redirect_tokens(word_chain, tmp_word, ch, params) ? - 1 : special_tokens(tmp_word, ch, params); + 1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params); } - +#if 0 static int ignore_spaces(int ch, struct param_type params) { return (ch == whitespace || ch == tab) && !params.escape_sequences; @@ -326,14 +385,7 @@ static int excessive_words(int ch, const struct param_type *params) } return 0; } - -static char** create_cmdline(const struct queue *word_chain, - int word_counter) -{ - char **cmdline = malloc((word_counter + 1) * sizeof(char*)); - queue_copy_words_to_args(word_chain, cmdline); - return cmdline; -} +#endif static int is_cd_command(const char *arg) { @@ -364,14 +416,6 @@ static void change_directory(char **cmdline) perror(path); } -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 open_files(const struct param_type *params, int *input_fd, int *output_fd) { @@ -385,18 +429,57 @@ static void open_files(const struct param_type *params, int *input_fd, O_APPEND, 0666); } -static void change_streams(int input_fd, int output_fd) +static void change_streams(int input_fd, int output_fd, int is_pipeline, + int is_begin_pipeline) { - if(input_fd) { + if((!is_pipeline && input_fd) || + (is_pipeline && is_begin_pipeline && input_fd)) { dup2(input_fd, 0); close(input_fd); } - if(output_fd) { + if((!is_pipeline && output_fd) || + (is_pipeline && !is_begin_pipeline && output_fd)) { dup2(output_fd, 1); close(output_fd); } } +static void set_signal_disposition(struct param_type *params) +{ + if(params->tokens == '&') + /* zombie process termination on signal */ + signal(SIGCHLD, handler); + else + /* the parent process will wait for the process to complete; + default signal disposition + */ + signal(SIGCHLD, SIG_DFL); +} + +static void wait_for_process_to_complete(struct p_queue *pid_store) +{ + int total_process_counter, current_process_counter, wait_pid; + total_process_counter = p_queue_get_process_quantity(pid_store); + current_process_counter = 0; + + do { + wait_pid = wait(NULL); + if(p_queue_find_pid(pid_store, wait_pid)) + ++current_process_counter; + } while(total_process_counter != current_process_counter); + + /* return of background process zombie cleanup */ + signal(SIGCHLD, handler); +} + +static void clean_up_memory(struct w_queue *word_chain, + struct c_queue *cmdlines, struct param_type *params) +{ + w_queue_clear(word_chain); + c_queue_clear(cmdlines); + clear_filename(params); +} + static void close_files(int input_fd, int output_fd) { if(input_fd) @@ -405,17 +488,94 @@ static void close_files(int input_fd, int output_fd) close(output_fd); } -static void run_external_program(struct queue *word_chain, +static void postprocessing(struct w_queue *word_chain, struct c_queue *cmdlines, + struct p_queue *pid_store, struct param_type *params, + int input_fd, int output_fd) +{ + clean_up_memory(word_chain, cmdlines, params); + close_files(input_fd, output_fd); + if(params->tokens != '&') + wait_for_process_to_complete(pid_store); + p_queue_clear(pid_store); +} + +static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines, + struct param_type *params, + int input_fd, int output_fd) +{ + struct p_queue pid_store; + p_queue_init(&pid_store); + char **cmdline = NULL; + int fd[2]; + int save_read_fd, pid; + + pipe(fd); + cmdline = c_queue_pop(cmdlines); + set_signal_disposition(params); + pid = fork(); + + if(pid == 0) { + close(fd[0]); + dup2(fd[1], 1); + close(fd[1]); + if(is_stream_redirection_set(params)) + change_streams(input_fd, output_fd, 1, 1); + execvp(cmdline[0], cmdline); + perror(cmdline[0]); + exit(1); + } + + close(fd[1]); + p_queue_push(&pid_store, pid); + + while(!c_queue_is_empty(cmdlines)) { + cmdline = c_queue_pop(cmdlines); + /* if not last process in pipeline*/ + if(!c_queue_is_empty(cmdlines)) { + save_read_fd = fd[0]; + pipe(fd); + } + pid = fork(); + if(pid == 0) { + if(!c_queue_is_empty(cmdlines)) { + dup2(save_read_fd, 0); + close(save_read_fd); + dup2(fd[1], 1); + close(fd[1]); + } else { + dup2(fd[0], 0); + close(fd[0]); + } + /* for last process in pipeline */ + if(c_queue_is_empty(cmdlines)) + if(is_stream_redirection_set(params)) + change_streams(input_fd, output_fd, 1, 0); + execvp(cmdline[0], cmdline); + perror(cmdline[0]); + exit(1); + } + if(!c_queue_is_empty(cmdlines)) { + close(save_read_fd); + close(fd[1]); + } else + close(fd[0]); + p_queue_push(&pid_store, pid); + } + postprocessing(word_chain, cmdlines, &pid_store, params, input_fd, + output_fd); +} + +static void run_external_program(struct w_queue *word_chain, + struct c_queue *cmdlines, struct param_type *params) { - int pid, wait_pid, result, input_fd, output_fd; + int pid, input_fd, output_fd; + struct p_queue pid_store; + p_queue_init(&pid_store); + char **cmdline = NULL; input_fd = 0; output_fd = 0; - if(word_chain->first == NULL) { - fprintf(stderr, "empty command\n"); - return; - } if(is_stream_redirection_set(params)) open_files(params, &input_fd, &output_fd); @@ -423,100 +583,131 @@ static void run_external_program(struct queue *word_chain, perror("can't open file"); return; } - char **cmdline = create_cmdline(word_chain, - queue_get_word_count(word_chain)); - if(is_cd_command(cmdline[0])) { - change_directory(cmdline); - clean_up_memory(word_chain, cmdline, params); + if(params->pipeline) { + make_pipeline(word_chain, cmdlines, params, input_fd, output_fd); + return; } + cmdline = c_queue_pop(cmdlines); + if(is_cd_command(cmdline[0])) + change_directory(cmdline); else { + set_signal_disposition(params); pid = fork(); if(pid == -1) { perror("fork error"); exit(1); } - if(params->tokens == '&') - /* zombie process termination on signal */ - signal(SIGCHLD, handler); - else - /* the parent process will wait for the process to complete; - default signal disposition - */ - signal(SIGCHLD, SIG_DFL); - /* child process */ if(pid == 0) { if(is_stream_redirection_set(params)) - change_streams(input_fd, output_fd); + change_streams(input_fd, output_fd, 0, 0); execvp(cmdline[0], cmdline); params->last_execution_status = 1; perror(cmdline[0]); exit(1); } - /* parent process */ - close_files(input_fd, output_fd); - clean_up_memory(word_chain, cmdline, params); - - /* waiting for forground process by pid*/ - if(params->tokens != '&') - do { - wait_pid = wait(&result); - } while(wait_pid != pid); - - /* return of background process zombie cleanup */ - signal(SIGCHLD, handler); + p_queue_push(&pid_store, pid); + postprocessing(word_chain, cmdlines, &pid_store, params, input_fd, + output_fd); } } static void command_processing(struct param_type *params, enum modes *current_mode, - struct queue *word_chain, + struct w_queue *word_chain, + struct c_queue *cmdlines, struct dynamic_array *tmp_word, int ch) { + char **cmdline = NULL; + /* and odd number of double quotes */ if(!is_double_quotes_pair(*params)) { print_error(); - reset_params(params, current_mode, word_chain, tmp_word); + reset_params(params, current_mode, word_chain, cmdlines, tmp_word); show_invitation(); return; } + if(filename_waiting(params)) { + if(params->wrong_command) + goto clean; + if(params->pipeline) { + if(params->tokens == '>' || params->tokens == append) + add_filename(tmp_word, params); + else if(params->tokens == '<') { + fprintf(stderr, "read in the last pipline element\n"); + goto clean; + } + } else + add_filename(tmp_word, params); + } else if(params->empty_word_flag || params->is_word) + add_word(word_chain, tmp_word, params); +#if 0 if(params->empty_word_flag || params->is_word) { - if(filename_waiting(params)) + if(filename_waiting(params)) { if(excessive_words(ch, params)) { fprintf(stderr, "too many args\n"); goto clean; + } + if(params->pipeline) { + if(params->tokens == '>' || params->tokens == append) + add_filename(tmp_word, params); + else if(params->tokens == '<') { + fprintf(stderr, "read in the last pipline element\n"); + goto clean; + } } else add_filename(tmp_word, params); - else + } else add_word(word_chain, tmp_word, params); + } else if(filename_waiting(params)) { fprintf(stderr, "filename expected\n"); goto clean; } +#endif if((params->tokens == and || params->tokens == or) && - params->last_execution_status == 1) + params->last_execution_status == 1) { fprintf(stderr, "cannot be performed\n"); - else - run_external_program(word_chain, params); + return; + } + + if(word_chain->first == NULL) { + fprintf(stderr, "empty command\n"); + goto clean; + } + + cmdline = create_cmdline(word_chain, w_queue_get_word_count(word_chain)); + c_queue_push(cmdlines, cmdline); + run_external_program(word_chain, cmdlines, params); clean: if(params->tokens == '&' || params->tokens == 0 || ch == new_line) show_invitation(); - reset_params(params, current_mode, word_chain, tmp_word); + reset_params(params, current_mode, word_chain, cmdlines, tmp_word); } +#if 0 static int is_empty_word(int ch, struct param_type params) { return (ch == whitespace || ch == tab) && !params.is_word && params.empty_word_flag; } +static int command_execution_condition(struct param_type *params) +{ + return + (!filename_waiting(params) && !params->pipeline) || + (params->pipeline && params->tokens == '&'); +} +#endif + static void word_separation_processing(int ch, struct param_type *params, enum modes *current_mode, - struct queue *word_chain, + struct w_queue *word_chain, + struct c_queue *cmdlines, struct dynamic_array *tmp_word) { /* could be a marker for the beginning of a blank word */ @@ -537,21 +728,22 @@ static void word_separation_processing(int ch, struct param_type *params, params->escape_sequences = 1; return; } - if(special_token_handling(word_chain, tmp_word, ch, params)) { - if(!filename_waiting(params)) - command_processing(params, current_mode, word_chain, tmp_word, ch); + if(special_token_handling(word_chain, cmdlines, tmp_word, ch, params)) { + if(command_execution_condition(params)) + command_processing(params, current_mode, word_chain, cmdlines, + tmp_word, ch); return; } if(params->wrong_command) { clean_input_buffer(); - reset_params(params, current_mode, word_chain, tmp_word); + reset_params(params, current_mode, word_chain, cmdlines, tmp_word); show_invitation(); return; } if(check_separation(ch, *params)) { if(excessive_words(ch, params)) { clean_input_buffer(); - reset_params(params, current_mode, word_chain, tmp_word); + reset_params(params, current_mode, word_chain, cmdlines, tmp_word); fprintf(stderr, "too many args\n"); show_invitation(); return; @@ -565,11 +757,10 @@ static void word_separation_processing(int ch, struct param_type *params, if(ignore_spaces(ch, *params)) return; - dynarr_push_back(tmp_word, ch); - params->is_word = 1; - params->escape_sequences = 0; + add_letter(ch, tmp_word, params); } +#if 0 static int escape_double_quotes_or_backslash(int ch, struct param_type params) { return params.escape_sequences && @@ -581,10 +772,11 @@ static int double_quotes_again(int ch, struct param_type params) return ch == double_quotes && !params.is_word && params.stored_symbol == '"'; } +#endif static void whole_word_processing(int ch, struct param_type *params, enum modes *current_mode, - struct queue *word_chain, + struct w_queue *word_chain, struct dynamic_array *tmp_word) { if(double_quotes_again(ch, *params)) { @@ -613,29 +805,32 @@ static void whole_word_processing(int ch, struct param_type *params, /* backslash recovery */ if(params->escape_sequences) dynarr_push_back(tmp_word, backslash); - dynarr_push_back(tmp_word, ch); - params->is_word = 1; - params->escape_sequences = 0; + + add_letter(ch, tmp_word, params); } int main() { int ch; struct param_type params; - struct queue word_chain; + struct w_queue word_chain; + struct c_queue cmdlines; struct dynamic_array tmp_word; enum modes current_mode = word_separation; - queue_init(&word_chain); + + w_queue_init(&word_chain); + c_queue_init(&cmdlines); dynarr_create_array(&tmp_word); init_params(¶ms, ¤t_mode); show_invitation(); + while((ch = getchar()) != EOF) { if(ch == new_line) command_processing(¶ms, ¤t_mode, &word_chain, - &tmp_word, ch); + &cmdlines, &tmp_word, ch); else if(current_mode == word_separation) word_separation_processing(ch, ¶ms, ¤t_mode, &word_chain, - &tmp_word); + &cmdlines, &tmp_word); else if(current_mode == whole_word) whole_word_processing(ch, ¶ms, ¤t_mode, &word_chain, &tmp_word); |