back to scratko.xyz
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-04-23 19:03:35 +0300
committerscratko <m@scratko.xyz>2024-05-25 21:12:30 +0300
commitc1e5cffb43977f5a2f8d9623e40c01dab6d80c46 (patch)
tree31c9c9b6847c292b13a354d962b86b6f5e15bb26
downloadshell-c1e5cffb43977f5a2f8d9623e40c01dab6d80c46.tar.gz
shell-c1e5cffb43977f5a2f8d9623e40c01dab6d80c46.tar.bz2
shell-c1e5cffb43977f5a2f8d9623e40c01dab6d80c46.zip
Shell-I releaseshell-I
-rw-r--r--Makefile17
-rw-r--r--dynamic_array.c43
-rw-r--r--dynamic_array.h20
-rw-r--r--queue.c44
-rw-r--r--queue.h19
-rw-r--r--shell.c241
6 files changed, 384 insertions, 0 deletions
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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <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(&params, &current_mode);
+ show_invitation();
+ while((ch = getchar()) != EOF) {
+ if(ch == new_line)
+ new_line_processing(&params, &current_mode, &word_chain,
+ &tmp_word);
+ else if(current_mode == word_separation)
+ word_separation_processing(ch, &params, &current_mode, &word_chain,
+ &tmp_word);
+ else if(current_mode == whole_word)
+ whole_word_processing(ch, &params, &current_mode, &word_chain,
+ &tmp_word);
+ }
+ putchar(new_line);
+ queue_clear(&word_chain);
+ dynarr_clear(&tmp_word);
+ return 0;
+}