From c1e5cffb43977f5a2f8d9623e40c01dab6d80c46 Mon Sep 17 00:00:00 2001 From: scratko Date: Tue, 23 Apr 2024 19:03:35 +0300 Subject: Shell-I release --- Makefile | 17 ++++ dynamic_array.c | 43 ++++++++++ dynamic_array.h | 20 +++++ queue.c | 44 +++++++++++ queue.h | 19 +++++ shell.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 384 insertions(+) create mode 100644 Makefile create mode 100644 dynamic_array.c create mode 100644 dynamic_array.h create mode 100644 queue.c create mode 100644 queue.h create mode 100644 shell.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..671c740 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +SRCMODULES = dynamic_array.c queue.c shell.c +OBJMODULES = $(SRCMODULES:.c=.o) +CC = gcc +CFLAGS = -Wall -g -c + +all: shell1 + +%.o: %.с %.h + $(CC) $(CFLAGS) $< -o $@ + +shell1: $(OBJMODULES) + $(CC) $(LIBS) $^ -o $@ + +-include deps.mk + +deps.mk: $(SRCMODULES) + $(CC) -MM $^ > $@ diff --git a/dynamic_array.c b/dynamic_array.c new file mode 100644 index 0000000..5a24cb8 --- /dev/null +++ b/dynamic_array.c @@ -0,0 +1,43 @@ +#include "dynamic_array.h" +#include + +void dynarr_create_array(struct dynamic_array *array) +{ + array->arr = calloc(initial_size, 1); + array->last_element_index = -1; + array->allocation_size = initial_size; +} + +static void dynarr_allocate_memory(struct dynamic_array *array) +{ + char *new_arr = calloc(array->allocation_size * 2, 1); + dynarr_copy_array(array, new_arr); + free(array->arr); + array->arr = new_arr; + array->allocation_size *= 2; +} + +void dynarr_push_back(struct dynamic_array *array, int letter) +{ + if(array->last_element_index+1 == array->allocation_size) + dynarr_allocate_memory(array); + ++array->last_element_index; + array->arr[array->last_element_index] = letter; +} +void dynarr_copy_array(struct dynamic_array *array, char *new_arr) +{ + int i; + for(i = 0; i <= array->last_element_index; ++i) + new_arr[i] = array->arr[i]; +} + +void dynarr_drop_word(struct dynamic_array *array) +{ + array->last_element_index = -1; +} + +void dynarr_clear(struct dynamic_array *array) +{ + free(array->arr); + array->arr = NULL; +} diff --git a/dynamic_array.h b/dynamic_array.h new file mode 100644 index 0000000..98491ea --- /dev/null +++ b/dynamic_array.h @@ -0,0 +1,20 @@ +#ifndef DYNAMIC_ARRAY_H_SENTRY +#define DYNAMIC_ARRAY_H_SENTRY + +enum { + initial_size = 10 +}; + +struct dynamic_array { + char *arr; + int last_element_index; + int allocation_size; +}; + +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_clear(struct dynamic_array *array); + +#endif diff --git a/queue.c b/queue.c new file mode 100644 index 0000000..861f1e5 --- /dev/null +++ b/queue.c @@ -0,0 +1,44 @@ +#include "queue.h" +#include + +void queue_init(struct queue *q) +{ + q->first = NULL; + q->last = NULL; +} + +void queue_push(struct queue *q, char *word) +{ + struct word_item *tmp = malloc(sizeof(struct word_item)); + tmp->word = word; + tmp->next = NULL; + if(!q->first) { + q->first = tmp; + q->last = q->first; + } else { + q->last->next = tmp; + q->last = q->last->next; + } +} + +void queue_clear(struct 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; +} + +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; + } +} diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..db8ac54 --- /dev/null +++ b/queue.h @@ -0,0 +1,19 @@ +#ifndef QUEUE_H_SENTRY +#define QUEUE_H_SENTRY + +struct word_item { + char *word; + struct word_item *next; +}; + +struct 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); +void queue_processing(const struct queue *q, void (*callback)(char*)); + +#endif diff --git a/shell.c b/shell.c new file mode 100644 index 0000000..f635c12 --- /dev/null +++ b/shell.c @@ -0,0 +1,241 @@ +#include "queue.h" +#include "dynamic_array.h" +#include +#include + +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; +} -- cgit v1.2.3