back to scratko.xyz
summaryrefslogtreecommitdiff
path: root/shell.c
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 /shell.c
downloadshell-I.tar.gz
shell-I.tar.bz2
shell-I.zip
Shell-I releaseshell-I
Diffstat (limited to 'shell.c')
-rw-r--r--shell.c241
1 files changed, 241 insertions, 0 deletions
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;
+}