diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | lexical_analysis.c | 214 | ||||
-rw-r--r-- | lexical_analysis.h | 84 | ||||
-rw-r--r-- | queue.c | 141 | ||||
-rw-r--r-- | queue.h | 66 | ||||
-rw-r--r-- | shell.c | 495 |
6 files changed, 825 insertions, 181 deletions
@@ -1,14 +1,14 @@ -SRCMODULES = dynamic_array.c queue.c shell.c +SRCMODULES = dynamic_array.c queue.c lexical_analysis.c shell.c OBJMODULES = $(SRCMODULES:.c=.o) CC = gcc CFLAGS = -Wall -g -c -all: shell-IV +all: shell-V %.o: %.с %.h $(CC) $(CFLAGS) $< -o $@ -shell-IV: $(OBJMODULES) +shell-V: $(OBJMODULES) $(CC) $(LIBS) $^ -o $@ -include deps.mk diff --git a/lexical_analysis.c b/lexical_analysis.c new file mode 100644 index 0000000..f931118 --- /dev/null +++ b/lexical_analysis.c @@ -0,0 +1,214 @@ +#include "lexical_analysis.h" +MAKE_QUEUE_PUSH(c_queue, cmdline, char**) + +int is_double_quotes_pair(struct param_type params) +{ + return !(params.double_quotes_counter % 2); +} + +int escape_double_quotes_or_backslash(int ch, struct param_type params) +{ + return params.escape_sequences && + (ch == double_quotes || ch == backslash); +} + +int double_quotes_again(int ch, struct param_type params) +{ + return ch == double_quotes && !params.is_word && + params.stored_symbol == '"'; +} + +int check_separation(int ch, struct param_type params) +{ + return (ch == whitespace || ch == tab) && params.is_word && + !params.escape_sequences; +} + +int ignore_spaces(int ch, struct param_type params) +{ + return (ch == whitespace || ch == tab) && !params.escape_sequences; +} + +int change_mode(int ch, struct param_type params) +{ + return ch == '"' && !params.escape_sequences; +} + +int start_escape_sequence(int ch, struct param_type params) +{ + return ch == backslash && !params.escape_sequences; +} + +int is_empty_word(int ch, struct param_type params) +{ + return (ch == whitespace || ch == tab) && !params.is_word && + params.empty_word_flag; +} + +int command_execution_condition(struct param_type *params) +{ + return + (!filename_waiting(params) && !params->pipeline) || + (params->pipeline && params->tokens == '&'); +} + +int is_special_token(int ch) +{ + return ch == and || ch == or || ch == '&' || ch == ';' || ch == '|'; +} + +int is_redirect_token(int ch, int next_ch) +{ + return ch == '<' || ch == '>' || (ch == '>' && next_ch == '>'); +} + +int excessive_words(int ch, struct param_type *params) +{ + int next_ch; + + 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; +} + +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); +} + +int validate_redirections(int ch, int next_ch, struct param_type *params) +{ + return (ch == '<' && params->streams.input_stream == NULL) || + ((ch == '>' || (ch == '>' && next_ch == '>')) && + params->streams.output_stream == NULL && + params->streams.output_stream_to_append == NULL); +} + +int is_double_token(struct param_type *params) +{ + return params->tokens == and || params->tokens == or || + params->tokens == append; +} + +/* + * redirection token verification + */ +int stream_redirect_tokens(struct w_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)) { + 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; + if(is_double_token(params)) + getchar(); + return 1; + } else { + fprintf(stderr, "syntax error\n"); + params->wrong_command = 1; + } + } + return 0; +} + +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 == '<')); +} + +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; +} + +/* + * verification of special tokens (|, &, &&, ||), except redirection tokens + */ +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)) { + 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; + } + return 0; +} diff --git a/lexical_analysis.h b/lexical_analysis.h new file mode 100644 index 0000000..e0f791a --- /dev/null +++ b/lexical_analysis.h @@ -0,0 +1,84 @@ +#ifndef LEXICAL_ANALYSIS_H_SENTRY +#define LEXICAL_ANALYSIS_H_SENTRY + +#include "queue.h" +#include "dynamic_array.h" + +#include <stdio.h> +#include <stdlib.h> + +enum { + new_line = 10, + whitespace = ' ', + tab = 9, + backslash = '\\', + double_quotes = '"' +}; + +/* two-letter tokens */ +enum { + append = '>' + 1, + and = '&' + 1, + 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; + 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 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); + +int is_double_quotes_pair(struct param_type params); +int escape_double_quotes_or_backslash(int ch, struct param_type params); +int double_quotes_again(int ch, struct param_type params); +int check_separation(int ch, struct param_type params); +int ignore_spaces(int ch, struct param_type params); +int change_mode(int ch, struct param_type params); +int start_escape_sequence(int ch, struct param_type params); +int is_empty_word(int ch, struct param_type params); +int command_execution_condition(struct param_type *params); + +int is_special_token(int ch); +int is_redirect_token(int ch, int next_ch); +int excessive_words(int ch, struct param_type *params); +void add_word_or_filename(struct w_queue *word_chain, + struct dynamic_array *tmp_word, + struct param_type *params); +int validate_redirections(int ch, int next_ch, struct param_type *params); +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 double_quotes_again(int ch, struct param_type params); +int pipeline_token_processing(struct w_queue *word_chain, + struct c_queue *cmdlines, + struct dynamic_array *tmp_word, + struct param_type *params); + +int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines, + struct dynamic_array *tmp_word, int ch, + struct param_type *params); +#endif @@ -1,13 +1,14 @@ #include "queue.h" #include <stdlib.h> -void queue_init(struct queue *q) +#if 0 +void w_queue_init(struct w_queue *q) { q->first = NULL; q->last = NULL; } -void queue_push(struct queue *q, char *word) +void w_queue_push(struct w_queue *q, char *word) { struct word_item *tmp = malloc(sizeof(struct word_item)); tmp->word = word; @@ -20,32 +21,19 @@ void queue_push(struct queue *q, char *word) q->last = q->last->next; } } - -void queue_clear(struct queue *q) +#endif +void w_queue_clear(struct w_queue *q) { struct word_item *tmp; while(q->first) { tmp = q->first; q->first = q->first->next; - free(tmp->word); free(tmp); } q->last = NULL; } -#if 0 -void queue_processing(const struct queue *q, void (*callback)(char*)) -{ - struct word_item *tmp; - tmp = q->first; - while(tmp) { - callback(tmp->word); - tmp = tmp->next; - } -} -#endif - -int queue_get_word_count(const struct queue *q) +int w_queue_get_word_count(const struct w_queue *q) { struct word_item *tmp; int counter; @@ -54,7 +42,7 @@ int queue_get_word_count(const struct queue *q) return counter; } -void queue_copy_words_to_args(const struct queue *q, char **cmdline) +void w_queue_copy_words_to_args(const struct w_queue *q, char **cmdline) { struct word_item *tmp; int mas_idx; @@ -62,3 +50,118 @@ void queue_copy_words_to_args(const struct queue *q, char **cmdline) cmdline[mas_idx] = tmp->word; cmdline[mas_idx] = NULL; } + +void c_queue_init(struct c_queue *q) +{ + q->first = NULL; + q->last = NULL; + q->last_extracted_item = NULL; +} + +#if 0 +void c_queue_push(struct c_queue *q, char **cmdline) +{ + struct cmdline_item *tmp = malloc(sizeof(struct cmdline_item)); + tmp->cmdline = cmdline; + tmp->next = NULL; + if(!q->first) { + q->first = tmp; + q->last = q->first; + } else { + q->last->next = tmp; + q->last = q->last->next; + } +} +#endif + +int c_queue_is_empty(struct c_queue *q) +{ + return q->last_extracted_item->next == NULL; +} + +char** c_queue_pop(struct c_queue *q) +{ + if(!q->last_extracted_item) { + if(q->first) { + q->last_extracted_item = q->first; + return q->last_extracted_item->cmdline; + } + return NULL; + } else { + if(q->last_extracted_item->next) { + q->last_extracted_item = q->last_extracted_item->next; + return q->last_extracted_item->cmdline; + } + return NULL; + } +} + +void c_queue_clear(struct c_queue *q) +{ + struct cmdline_item *tmp = NULL; + while(q->first) { + int i; + for(i = 0; q->first->cmdline[i]; ++i) + free(q->first->cmdline[i]); + free(q->first->cmdline); + tmp = q->first; + q->first = q->first->next; + free(tmp); + } + q->last = NULL; + q->last_extracted_item = NULL; +} +#if 0 +void p_queue_init(struct p_queue *q) +{ + q->first = NULL; + q->last = NULL; +} + +void p_queue_push(struct p_queue *q, int pid) +{ + struct pid_item *tmp = malloc(sizeof(struct pid_item)); + tmp->pid = pid; + tmp->next = NULL; + if(!q->first) { + q->first = tmp; + q->last = q->first; + } else { + q->last->next = tmp; + q->last = q->last->next; + } +} +#endif + +int p_queue_find_pid(struct p_queue *q, int pid) +{ + struct pid_item *tmp = q->first; + while(tmp) { + if(tmp->pid == pid) + return 1; + tmp = tmp->next; + } + return 0; +} + +int p_queue_get_process_quantity(struct p_queue *q) +{ + int counter = 0; + struct pid_item *tmp = q->first; + while(tmp) { + ++counter; + tmp = tmp->next; + } + return counter; +} + +void p_queue_clear(struct p_queue *q) +{ + struct pid_item *tmp = NULL; + while(q->first) { + tmp = q->first; + q->first = q->first->next; + free(tmp); + } + q->last = NULL; +} @@ -1,23 +1,71 @@ #ifndef QUEUE_H_SENTRY #define QUEUE_H_SENTRY +#define MAKE_QUEUE_INIT(NAME) \ + void NAME ## _init(struct NAME *q) \ +{ \ + q->first = NULL; \ + q->last = NULL; \ +} + +#define MAKE_QUEUE_PUSH(NAME, VALUE, TYPE) \ + void NAME ## _push(struct NAME *q, TYPE VALUE) \ +{ \ + struct VALUE ## _item *tmp = malloc(sizeof(struct VALUE ## _item)); \ + tmp-> VALUE = VALUE; \ + tmp->next = NULL; \ + if(!q->first) { \ + q->first = tmp; \ + q->last = q->first; \ + } else { \ + q->last->next = tmp; \ + q->last = q->last->next; \ + } \ +} + struct word_item { char *word; struct word_item *next; }; -struct queue { +struct cmdline_item { + char **cmdline; + struct cmdline_item *next; +}; + +struct pid_item { + int pid; + struct pid_item *next; +}; + +struct w_queue { struct word_item *first; struct word_item *last; }; -void queue_init(struct queue *q); -void queue_push(struct queue *q, char *word); -void queue_clear(struct queue *q); -#if 0 -void queue_processing(const struct queue *q, void (*callback)(char*)); -#endif -int queue_get_word_count(const struct queue *q); -void queue_copy_words_to_args(const struct queue *q, char **cmdline); +struct c_queue { + struct cmdline_item *first; + struct cmdline_item *last; + struct cmdline_item *last_extracted_item; +}; + +struct p_queue { + struct pid_item *first; + struct pid_item *last; +}; + +void w_queue_clear(struct w_queue *q); +int w_queue_get_word_count(const struct w_queue *q); +void w_queue_copy_words_to_args(const struct w_queue *q, char **cmdline); + +void c_queue_init(struct c_queue *q); +void c_queue_push(struct c_queue *q, char **cmdline); +char** c_queue_pop(struct c_queue *q); +void c_queue_clear(struct c_queue *q); +int c_queue_is_empty(struct c_queue *q); + +int p_queue_find_pid(struct p_queue *q, int pid); +void p_queue_clear(struct p_queue *q); +int p_queue_get_process_quantity(struct p_queue *q); #endif @@ -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); |