diff options
Diffstat (limited to 'shell.c')
-rw-r--r-- | shell.c | 241 |
1 files changed, 241 insertions, 0 deletions
@@ -0,0 +1,241 @@ +#include "queue.h" +#include "dynamic_array.h" +#include <stdio.h> +#include <stdlib.h> + +enum modes { word_separation, whole_word }; +enum { + new_line = 10, + whitespace = ' ', + tab = 9, + backslash = '\\', + double_quotes = '"' +}; + +struct param_type { + int is_word; + int escape_sequences; + unsigned int double_quotes_counter; + char stored_symbol; + int empty_word_flag; +}; + +static void show_invitation() +{ + printf("> "); +} + +static void print_error() +{ + fprintf(stderr, "Error: unmatched quotes\n"); +} + +static void init_params(struct param_type *params, enum modes *current_mode) +{ + params->is_word = 0; + params->escape_sequences = 0; + params->double_quotes_counter = 0; + params->stored_symbol = ' '; + params->empty_word_flag = 0; + *current_mode = word_separation; +} + +static void prepare_new_line(struct param_type *params, + 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); +} + +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 ignore_spaces(int ch, struct param_type params) +{ + return (ch == whitespace || ch == tab) && !params.escape_sequences; +} + +static int change_mode(int ch, struct param_type params) +{ + return ch == '"' && !params.escape_sequences; +} + +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) +{ + 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); + tmp_word->last_element_index = -1; + params->is_word = 0; +} + +static void print_word(char *word) +{ + putchar('['); + while(*word) { + putchar(*word); + ++word; + } + printf("]\n"); +} + +static void print_line(const struct queue *word_chain) +{ + queue_processing(word_chain, print_word); +} + +static void new_line_processing(struct param_type *params, + enum modes *current_mode, + struct queue *word_chain, + struct dynamic_array *tmp_word) +{ + /* and odd number of double quotes */ + if(!is_double_quotes_pair(*params)) { + print_error(); + prepare_new_line(params, current_mode, word_chain, tmp_word); + 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); + } + print_line(word_chain); + prepare_new_line(params, current_mode, word_chain, tmp_word); + show_invitation(); +} + +static int is_empty_word(int ch, struct param_type params) +{ + return (ch == whitespace || ch == tab) && !params.is_word && params.empty_word_flag; +} + +static void word_separation_processing(int ch, struct param_type *params, + enum modes *current_mode, + struct queue *word_chain, + struct dynamic_array *tmp_word) +{ + /* could be a marker for the beginning of a blank word */ + params->stored_symbol = ch; + if(is_empty_word(ch, *params)) { + dynarr_push_back(tmp_word, '\0'); + add_word(word_chain, tmp_word, params); + params->empty_word_flag = 0; + return; + } + params->empty_word_flag = 0; + if(change_mode(ch, *params)) { + ++params->double_quotes_counter; + *current_mode = whole_word; + return; + } + if(start_escape_sequence(ch, *params)) { + params->escape_sequences = 1; + return; + } + if(check_separation(ch, *params)) { + add_word(word_chain, tmp_word, params); + params->is_word = 0; + return; + } + if(ignore_spaces(ch, *params)) + return; + + dynarr_push_back(tmp_word, ch); + params->is_word = 1; + params->escape_sequences = 0; +} + +static int escape_double_quotes_or_backslash(int ch, struct param_type params) +{ + return params.escape_sequences && + (ch == double_quotes || ch == backslash); +} + +static int double_quotes_again(int ch, struct param_type params) +{ + return ch == double_quotes && !params.is_word && + params.stored_symbol == '"'; +} + +static void whole_word_processing(int ch, struct param_type *params, + enum modes *current_mode, + struct queue *word_chain, + struct dynamic_array *tmp_word) +{ + if(double_quotes_again(ch, *params)) { + params->stored_symbol = '\0'; + params->empty_word_flag = 1; + ++params->double_quotes_counter; + *current_mode = word_separation; + return; + } + params->stored_symbol = '\0'; + if(change_mode(ch, *params)) { + ++params->double_quotes_counter; + *current_mode = word_separation; + return; + } + if(escape_double_quotes_or_backslash(ch, *params)) { + dynarr_push_back(tmp_word, ch); + params->escape_sequences = 0; + return; + } + if(start_escape_sequence(ch, *params)) { + params->escape_sequences = 1; + return; + } + /* 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; +} + +int main() +{ + int ch; + struct param_type params; + struct queue word_chain; + struct dynamic_array tmp_word; + enum modes current_mode = word_separation; + queue_init(&word_chain); + dynarr_create_array(&tmp_word); + init_params(¶ms, ¤t_mode); + show_invitation(); + while((ch = getchar()) != EOF) { + if(ch == new_line) + new_line_processing(¶ms, ¤t_mode, &word_chain, + &tmp_word); + else if(current_mode == word_separation) + word_separation_processing(ch, ¶ms, ¤t_mode, &word_chain, + &tmp_word); + else if(current_mode == whole_word) + whole_word_processing(ch, ¶ms, ¤t_mode, &word_chain, + &tmp_word); + } + putchar(new_line); + queue_clear(&word_chain); + dynarr_clear(&tmp_word); + return 0; +} |