back to scratko.xyz
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-06-20 18:38:11 +0300
committerscratko <m@scratko.xyz>2024-06-23 23:25:46 +0300
commit74c6a747a58b38131534556ab95fa4dd4b514780 (patch)
tree68dae86f9ee994c2bc13681703a8c69e0e2b5f51
parent8f4f87eabec13330a2b3a974975053c1e4632a11 (diff)
downloadshell-edit.tar.gz
shell-edit.tar.bz2
shell-edit.zip
Shell-edit releaseshell-edit
Autocomplete program and file names (tab). Moving the cursor to edit commands (left and right arrows). Deleting a character in a command (backspace).
-rw-r--r--Makefile6
-rw-r--r--dynamic_array.c3
-rw-r--r--dynamic_array.h2
-rw-r--r--file_suggestions.c258
-rw-r--r--file_suggestions.h12
-rw-r--r--lexical_analysis.c28
-rw-r--r--lexical_analysis.h6
-rw-r--r--readline.c153
-rw-r--r--readline.h37
-rw-r--r--shell.c173
-rw-r--r--shell.h64
11 files changed, 700 insertions, 42 deletions
diff --git a/Makefile b/Makefile
index 8a49831..2bbac5e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,14 @@
-SRCMODULES = dynamic_array.c queue.c lexical_analysis.c shell.c
+SRCMODULES = dynamic_array.c queue.c lexical_analysis.c file_suggestions.c readline.c shell.c
OBJMODULES = $(SRCMODULES:.c=.o)
CC = gcc
CFLAGS = -Wall -g -c
-all: shell-V
+all: shell-edit
%.o: %.с %.h
$(CC) $(CFLAGS) $< -o $@
-shell-V: $(OBJMODULES)
+shell-edit: $(OBJMODULES)
$(CC) $(LIBS) $^ -o $@
-include deps.mk
diff --git a/dynamic_array.c b/dynamic_array.c
index 5a24cb8..c0a8723 100644
--- a/dynamic_array.c
+++ b/dynamic_array.c
@@ -1,4 +1,5 @@
#include "dynamic_array.h"
+
#include <stdlib.h>
void dynarr_create_array(struct dynamic_array *array)
@@ -31,7 +32,7 @@ void dynarr_copy_array(struct dynamic_array *array, char *new_arr)
new_arr[i] = array->arr[i];
}
-void dynarr_drop_word(struct dynamic_array *array)
+void dynarr_reset_array(struct dynamic_array *array)
{
array->last_element_index = -1;
}
diff --git a/dynamic_array.h b/dynamic_array.h
index 98491ea..ae63fc4 100644
--- a/dynamic_array.h
+++ b/dynamic_array.h
@@ -14,7 +14,7 @@ struct dynamic_array {
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_reset_array(struct dynamic_array *array);
void dynarr_clear(struct dynamic_array *array);
#endif
diff --git a/file_suggestions.c b/file_suggestions.c
new file mode 100644
index 0000000..528d717
--- /dev/null
+++ b/file_suggestions.c
@@ -0,0 +1,258 @@
+#include "file_suggestions.h"
+#include "readline.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+extern char **environ;
+
+static void clean_name_and_path(char *name, char *path)
+{
+ if(name)
+ free(name);
+ if(strcmp(path, "."))
+ free(path);
+}
+
+static int print_all_filenames(const char *name, const char *path)
+{
+ DIR *dir;
+ struct dirent *dent;
+ char *next_filename;
+ int first_suggestion;
+
+ first_suggestion = 1;
+
+ dir = opendir(path);
+ if(!dir) {
+ perror(path);
+ return 0;
+ }
+ while((dent = readdir(dir)) != NULL) {
+ next_filename = dent->d_name;
+ if(first_suggestion) {
+ putchar('\n');
+ first_suggestion = 0;
+ }
+ printf("%s\n", next_filename);
+ }
+ closedir(dir);
+ return 1;
+}
+
+static int matchmaking(const char *name, const char *path)
+{
+ DIR *dir;
+ struct dirent *dent;
+ char *next_filename;
+ int mismatch_status, first_suggestion, something_found;
+
+ mismatch_status = 0;
+ something_found = 0;
+ first_suggestion = 1;
+
+ dir = opendir(path);
+ if(!dir) {
+ perror(path);
+ return 0;
+ }
+ while((dent = readdir(dir)) != NULL) {
+ next_filename = dent->d_name;
+ int i;
+ for(i = 0; name[i] && next_filename[i]; ++i) {
+ if(name[i] != next_filename[i]) {
+ mismatch_status = 1;
+ break;
+ }
+ }
+ if(!mismatch_status && name[i] == '\0') {
+ if(first_suggestion) {
+ putchar('\n');
+ first_suggestion = 0;
+ }
+ printf("%s\n", next_filename);
+ if(!something_found)
+ something_found = 1;
+ }
+ mismatch_status = 0;
+ }
+ closedir(dir);
+ return something_found;
+}
+
+static void extract_path_and_name(char **path, char **name,
+ struct readline_type *readline,
+ int start_filename_idx)
+{
+ int i, j, k;
+
+ for(i = readline->last_element_index; i >= start_filename_idx; --i) {
+ if(readline->arr[i] == '/')
+ break;
+ }
+ *path = malloc(i - start_filename_idx+ 2);
+ *name = malloc(readline->last_element_index - i + 1);
+
+ /* extract path */
+ for(j = start_filename_idx, k = 0; j <= i; ++j, ++k)
+ (*path)[k] = readline->arr[j];
+ (*path)[k] = '\0';
+
+ /* extract name */
+ for(j = i + 1, k = 0; j <= readline->last_element_index; ++j, ++k)
+ (*name)[k] = readline->arr[j];
+ (*name)[k] = '\0';
+}
+
+static void copy_filename(char *dest, char *source, int start_idx, int last_idx)
+{
+ int i, j;
+ for(i = 0, j = start_idx; j <= last_idx; ++i, ++j)
+ dest[i] = source[j];
+}
+
+static void extract_only_filename(char **name, struct readline_type *readline,
+ int start_filename_idx)
+{
+ int size = readline->last_element_index - start_filename_idx + 2;
+ *name = malloc(size);
+ copy_filename(*name, readline->arr, start_filename_idx,
+ readline->last_element_index);
+ (*name)[size-1] = '\0';
+}
+
+int get_filename_match(struct readline_type *readline, int start_filename_idx)
+{
+ int extract_path_status, match_result;
+
+ char *name = NULL;
+ extract_path_status = 0;
+ match_result = 0;
+ /* default path */
+ char *path = ".";
+ int i;
+
+ for(i = start_filename_idx; i <= readline->last_element_index; ++i) {
+ if(readline->arr[i] == '/') {
+ extract_path_status = 1;
+ break;
+ }
+ }
+ /* not empty filename */
+ if(readline->arr[start_filename_idx] != ' ')
+ extract_path_status ? extract_path_and_name(&path, &name, readline,
+ start_filename_idx) :
+ extract_only_filename(&name, readline,
+ start_filename_idx);
+ match_result =
+ (name == NULL && !strcmp(path, ".")) ? print_all_filenames(name, path) :
+ matchmaking(name, path);
+ clean_name_and_path(name, path);
+ return match_result;
+}
+
+static int is_token(int ch)
+{
+ return ch == ' ' || ch == '|' || ch == '&' || ch == ';' || ch == '<' ||
+ ch == '>';
+}
+
+int get_start_filename_idx(struct readline_type *readline)
+{
+ /* empty filename */
+ if(readline->last_element_index == ' ')
+ return readline->last_element_index;
+
+ int i;
+ for(i = readline->last_element_index; i != -1; --i) {
+ if(is_token(readline->arr[i]))
+ return ++i;
+ }
+ return 0;
+}
+
+/*
+ * 1 searching for suggestions for file name,
+ * 0 searching for program name from PATH
+ */
+int suggestions_for_filename(struct readline_type *readline)
+{
+ int token_status, i;
+ token_status = 0;
+
+ for(i = readline->last_element_index; i != -1; --i) {
+ if(is_token(readline->arr[i])) {
+ token_status = 1;
+ continue;
+ }
+ if(token_status && !is_token(readline->arr[i]))
+ return 1;
+ }
+ return 0;
+}
+
+int find_separator_index(char *path, int begin_idx)
+{
+ int i;
+ for(i = begin_idx; path[i] != ':' && path[i]; ++i)
+ {}
+ return i;
+}
+
+int get_program_name_match(struct readline_type *readline)
+{
+ char *name = NULL;
+ char *path = NULL;
+ char *path_variable = NULL;
+ int size, begin_idx, end_idx, result;
+
+ begin_idx = 0;
+ end_idx = 0;
+ result = 0;
+ size = readline->last_element_index + 2;
+ name = malloc(size);
+
+ /* extracting prog name */
+ int i;
+ for(i = 0; i <= readline->last_element_index; ++i)
+ name[i] = readline->arr[i];
+ name[i] = '\0';
+
+ /* searching PATH */
+ for(i = 0; environ[i]; ++i)
+ if(!strncmp(environ[i], "PATH", 4)) {
+ path_variable = environ[i];
+ break;
+ }
+ if(!path_variable)
+ return 0;
+
+ /* skipping PATH= */
+ path_variable = path_variable + 5;
+
+ /* directory search by separator (:) */
+ end_idx = find_separator_index(path_variable, begin_idx);
+ size = end_idx + 1;
+ path = malloc(size);
+ copy_filename(path, path_variable, begin_idx, end_idx - 1);
+ path[end_idx] = '\0';
+ result = matchmaking(name, path) ? 1 : result;
+ free(path);
+
+ /* if end_idx is beginning with ':' */
+ while(path_variable[end_idx]) {
+ begin_idx = end_idx + 1;
+ end_idx = find_separator_index(path_variable, begin_idx);
+ size = end_idx + 1;
+ path = malloc(size);
+ copy_filename(path, path_variable, begin_idx, end_idx - 1);
+ path[end_idx] = '\0';
+ result = matchmaking(name, path) ? 1 : result;
+ free(path);
+ }
+ free(name);
+ return result;
+}
diff --git a/file_suggestions.h b/file_suggestions.h
new file mode 100644
index 0000000..1b818a8
--- /dev/null
+++ b/file_suggestions.h
@@ -0,0 +1,12 @@
+#ifndef FILE_SUGGESTIONS_H_SENTRY
+#define FILE_SUGGESTIONS_H_SENTRY
+
+#include "readline.h"
+
+int get_filename_match(struct readline_type *readline, int start_filename_idx);
+int get_program_name_match(struct readline_type *readline);
+
+int get_start_filename_idx(struct readline_type *readline);
+int suggestions_for_filename(struct readline_type *readline);
+
+#endif
diff --git a/lexical_analysis.c b/lexical_analysis.c
index 4349a69..5db57cb 100644
--- a/lexical_analysis.c
+++ b/lexical_analysis.c
@@ -123,11 +123,13 @@ int is_redirect_token(int ch, int next_ch)
*/
int stream_redirect_tokens(struct w_queue *word_chain,
struct dynamic_array *tmp_word, int ch,
- struct param_type *params)
+ struct param_type *params,
+ struct readline_type *readline)
{
int next_ch;
- next_ch = getchar();
- ungetc(next_ch, stdin);
+
+ if(ch == '>')
+ next_ch = readline->arr[readline->considered_index + 1];
if(is_redirect_token(ch, next_ch)) {
add_word_or_filename(word_chain, tmp_word, params);
@@ -138,7 +140,7 @@ int stream_redirect_tokens(struct w_queue *word_chain,
if(validate_redirections(ch, next_ch, params)) {
params->tokens = (ch == '>' && next_ch == '>') ? append : ch;
if(is_double_token(params))
- getchar();
+ ++readline->considered_index;
return 1;
} else
params->wrong_command = err_redirect_stream_again;
@@ -178,7 +180,7 @@ int pipeline_token_processing(struct w_queue *word_chain,
c_queue_push(cmdlines, cmdline);
w_queue_clear(word_chain);
- dynarr_drop_word(tmp_word);
+ dynarr_reset_array(tmp_word);
return 1;
}
@@ -187,11 +189,12 @@ int pipeline_token_processing(struct w_queue *word_chain,
*/
int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines,
struct dynamic_array *tmp_word, int ch,
- struct param_type *params)
+ struct param_type *params, struct readline_type *readline)
{
- int next_ch;
- next_ch = getchar();
- ungetc(next_ch, stdin);
+ int next_ch, i;
+
+ if(ch == '|' || ch == '&')
+ next_ch = readline->arr[readline->considered_index + 1];
if(is_special_token(ch, next_ch)) {
add_word_or_filename(word_chain, tmp_word, params);
@@ -208,8 +211,9 @@ int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines,
} else if(ch == '&' && next_ch == '&')
params->tokens = and;
else if(ch == '&') {
- while((ch = getchar()) != new_line) {
- if(ch != whitespace && ch != tab) {
+ for(i = readline->considered_index+1; readline->arr[i] != new_line;
+ ++i) {
+ if(readline->arr[i] != whitespace && readline->arr[i] != tab) {
params->wrong_command = err_bg_process;
return 0;
}
@@ -217,7 +221,7 @@ int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines,
params->tokens = '&';
}
if(is_double_token(params))
- getchar();
+ ++readline->considered_index;
return 1;
}
return 0;
diff --git a/lexical_analysis.h b/lexical_analysis.h
index 3266ad7..9d7d556 100644
--- a/lexical_analysis.h
+++ b/lexical_analysis.h
@@ -2,6 +2,7 @@
#define LEXICAL_ANALYSIS_H_SENTRY
#include "shell.h"
+#include "readline.h"
int is_double_quotes_pair(struct param_type params);
int escape_double_quotes_or_backslash(int ch, struct param_type params);
@@ -26,7 +27,8 @@ 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 ch, struct param_type *params,
+ struct readline_type *readline);
int double_quotes_again(int ch, struct param_type params);
@@ -37,6 +39,6 @@ int pipeline_token_processing(struct w_queue *word_chain,
int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines,
struct dynamic_array *tmp_word, int ch,
- struct param_type *params);
+ struct param_type *params, struct readline_type *readline);
#endif
diff --git a/readline.c b/readline.c
new file mode 100644
index 0000000..c36b19a
--- /dev/null
+++ b/readline.c
@@ -0,0 +1,153 @@
+#include "readline.h"
+#include "shell.h"
+
+#include <stdlib.h>
+#include <string.h> /* memmove */
+#include <stdio.h>
+
+void readline_create_array(struct readline_type *readline)
+{
+ readline->arr = calloc(initial_size, 1);
+ readline->cursor_pos = 0;
+ readline->last_element_index = -1;
+ readline->considered_index = 0;
+ readline->allocation_size = initial_size;
+}
+
+static void readline_copy_array(struct readline_type *readline, char *new_arr)
+{
+ int i;
+ for(i = 0; i <= readline->last_element_index; ++i)
+ new_arr[i] = readline->arr[i];
+}
+
+static void readline_allocate_memory(struct readline_type *readline)
+{
+ char *new_arr = calloc(readline->allocation_size * 2, 1);
+ readline_copy_array(readline, new_arr);
+ free(readline->arr);
+ readline->arr = new_arr;
+ readline->allocation_size *= 2;
+}
+
+static void readline_reprint_modified_part(struct readline_type *readline,
+ int begin_idx)
+{
+ int i, counter;
+ for(i = begin_idx, counter = 0; i <= readline->last_element_index;
+ ++i, ++counter)
+ putchar(readline->arr[i]);
+
+ /* return cursor to previous position */
+ for( ; counter != 0; --counter)
+ putchar('\b');
+ fflush(stdout);
+}
+
+void readline_add_char(struct readline_type *readline, int ch)
+{
+ char *begin_arr;
+ int size;
+
+ if(readline->last_element_index+1 == readline->allocation_size)
+ readline_allocate_memory(readline);
+ /* insert in middle position */
+ if(readline->cursor_pos - 1 != readline->last_element_index) {
+ begin_arr = readline->arr + readline->cursor_pos;
+ size = readline->last_element_index - readline->cursor_pos + 1;
+ memmove(begin_arr+1, begin_arr, size);
+ readline->arr[readline->cursor_pos] = ch;
+ ++readline->last_element_index;
+ readline_reprint_modified_part(readline, readline->cursor_pos + 1);
+ } else {
+ ++readline->last_element_index;
+ readline->arr[readline->last_element_index] = ch;
+ }
+ ++readline->cursor_pos;
+}
+
+void readline_reset_array(struct readline_type *readline)
+{
+ readline->last_element_index = -1;
+ readline->cursor_pos = 0;
+}
+
+void readline_clear(struct readline_type *readline)
+{
+ free(readline->arr);
+ readline->arr = NULL;
+}
+
+void readline_print(struct readline_type *readline)
+{
+ int i;
+ for(i = 0; i <= readline->last_element_index; ++i)
+ putchar(readline->arr[i]);
+ readline->cursor_pos = readline->last_element_index + 1;
+ fflush(stdout);
+}
+
+enum keys readline_detect_arrow_keys(int ch)
+{
+ char next_ch;
+ if(ch == esc) {
+ next_ch = getchar();
+ if(next_ch != '[')
+ return unknown_key;
+ next_ch = getchar();
+
+ switch(next_ch) {
+ case left_arrow:
+ return left_arrow;
+ case right_arrow:
+ return right_arrow;
+ case up_arrow:
+ return up_arrow;
+ case down_arrow:
+ return down_arrow;
+ default:
+ return unknown_key;
+ }
+ }
+ return none;
+}
+
+void readline_move_along(struct readline_type *readline, enum keys found_key)
+{
+ switch(found_key) {
+ case left_arrow:
+ if(readline->cursor_pos > 0) {
+ putchar('\b');
+ fflush(stdout);
+ --readline->cursor_pos;
+ }
+ break;
+ case right_arrow:
+ if(readline->cursor_pos <= readline->last_element_index) {
+ putchar(readline->arr[readline->cursor_pos]);
+ fflush(stdout);
+ ++readline->cursor_pos;
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+void readline_character_deletion(struct readline_type *readline)
+{
+ char *begin_arr;
+ int size;
+
+ if(readline->cursor_pos > 0) {
+ putchar('\b');
+ fflush(stdout);
+ begin_arr = readline->arr + readline->cursor_pos;
+ size = readline->last_element_index - readline->cursor_pos + 1;
+ memmove(begin_arr-1, begin_arr, size);
+ readline->arr[readline->last_element_index] = ' ';
+ readline_reprint_modified_part(readline, readline->cursor_pos - 1);
+ --readline->last_element_index;
+ --readline->cursor_pos;
+ }
+}
diff --git a/readline.h b/readline.h
new file mode 100644
index 0000000..b626cae
--- /dev/null
+++ b/readline.h
@@ -0,0 +1,37 @@
+#ifndef READLINE_H_SENTRY
+#define READLINE_H_SENTRY
+
+#include "readline.h"
+
+/*
+ * ends with these codes in escape sequence
+ * for the arrows
+ */
+enum keys{
+ left_arrow = 'D',
+ right_arrow = 'C',
+ up_arrow = 'A',
+ down_arrow = 'B',
+ none = 0,
+ unknown_key = -1,
+ backspace = 127
+};
+
+struct readline_type {
+ char *arr;
+ int cursor_pos;
+ int considered_index;
+ int last_element_index;
+ int allocation_size;
+};
+
+void readline_create_array(struct readline_type *readline);
+void readline_add_char(struct readline_type *readline, int ch);
+void readline_reset_array(struct readline_type *readline);
+void readline_clear(struct readline_type *readline);
+void readline_print(struct readline_type *readline_type);
+enum keys readline_detect_arrow_keys(int ch);
+void readline_move_along(struct readline_type *readline, enum keys found_key);
+void readline_character_deletion(struct readline_type *readline);
+
+#endif
diff --git a/shell.c b/shell.c
index 71be7fd..2944f00 100644
--- a/shell.c
+++ b/shell.c
@@ -2,6 +2,8 @@
#include "queue.h"
#include "dynamic_array.h"
#include "lexical_analysis.h"
+#include "file_suggestions.h"
+#include "readline.h"
#include <stdio.h>
#include <stdlib.h>
@@ -11,6 +13,7 @@
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
+#include <termios.h>
MAKE_QUEUE_INIT(w_queue)
MAKE_QUEUE_INIT(p_queue)
@@ -23,6 +26,7 @@ enum modes { word_separation, whole_word };
static void show_invitation()
{
printf("> ");
+ fflush(stdout);
}
/* remove background zombie processes */
@@ -54,6 +58,7 @@ static void init_params(struct param_type *params, enum modes *current_mode)
/* 0 - success, 1 - error */
params->last_execution_status = 0;
params->pipeline = 0;
+ params->new_readline = 1;
*current_mode = word_separation;
}
@@ -74,11 +79,13 @@ static void reset_params(struct param_type *params,
enum modes *current_mode,
struct w_queue *word_chain,
struct c_queue *cmdlines,
- struct dynamic_array *tmp_word)
+ struct dynamic_array *tmp_word,
+ struct readline_type *readline)
{
w_queue_clear(word_chain);
c_queue_clear(cmdlines);
- dynarr_drop_word(tmp_word);
+ dynarr_reset_array(tmp_word);
+ readline_reset_array(readline);
clear_filename(params);
init_params(params, current_mode);
}
@@ -126,11 +133,13 @@ void error_identification(const 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)
+ struct param_type *params,
+ struct readline_type *readline)
{
return
- stream_redirect_tokens(word_chain, tmp_word, ch, params) ?
- 1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params);
+ stream_redirect_tokens(word_chain, tmp_word, ch, params, readline) ?
+ 1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params,
+ readline);
}
static void wait_for_process_to_complete(struct p_queue *pid_store)
@@ -394,7 +403,7 @@ void add_filename(struct dynamic_array *tmp_word, struct param_type *params)
malloc(tmp_word->last_element_index+1);
dynarr_copy_array(tmp_word, params->streams.output_stream_to_append);
}
- dynarr_drop_word(tmp_word);
+ dynarr_reset_array(tmp_word);
params->is_word = 0;
}
@@ -405,7 +414,7 @@ void add_word(struct w_queue *word_chain, struct dynamic_array *tmp_word,
char *word = malloc(tmp_word->last_element_index+1);
dynarr_copy_array(tmp_word, word);
w_queue_push(word_chain, word);
- dynarr_drop_word(tmp_word);
+ dynarr_reset_array(tmp_word);
params->is_word = 0;
}
@@ -416,13 +425,86 @@ char** create_cmdline(const struct w_queue *word_chain, int word_counter)
return cmdline;
}
+static int readline_termination_condition(int ch,
+ struct readline_type *readline)
+{
+ return
+ ch == new_line ||
+ (ch == end_of_file && readline->last_element_index == -1);
+}
+
+static void echo_characters(int ch)
+{
+ write(1, &ch, 1);
+}
+
+static void generate_readline(struct readline_type *readline)
+{
+ enum keys found_key = none;
+ int match_result;
+ char ch;
+
+ while((ch = getchar()) && !readline_termination_condition(ch, readline)) {
+ if(ch == tab) {
+ if(suggestions_for_filename(readline)) {
+ match_result = get_filename_match(readline,
+ get_start_filename_idx(readline));
+ if(!match_result)
+ echo_characters(new_line);
+ show_invitation();
+ if(readline->last_element_index != -1)
+ readline_print(readline);
+ /* program name is not empty */
+ } else if(readline->last_element_index != -1) {
+ match_result = get_program_name_match(readline);
+ if(!match_result)
+ echo_characters(new_line);
+ show_invitation();
+ if(readline->last_element_index != -1)
+ readline_print(readline);
+ }
+ continue;
+ }
+ if(ch == end_of_file)
+ continue;
+
+ /* moving */
+ found_key = readline_detect_arrow_keys(ch);
+ if(found_key != none && found_key != unknown_key) {
+ readline_move_along(readline, found_key);
+ continue;
+ }
+ if(found_key == unknown_key)
+ continue;
+
+ /* deletion */
+ if(ch == backspace) {
+ readline_character_deletion(readline);
+ continue;
+ }
+
+ echo_characters(ch);
+ readline_add_char(readline, ch);
+ }
+ /* moving cursor to end */
+ if(ch == new_line)
+ readline->cursor_pos = readline->last_element_index + 1;
+ /* adding '\n' to readline */
+ readline_add_char(readline, ch);
+ /* print '\n' to end of readline */
+ if(ch == new_line)
+ echo_characters(ch);
+}
+
static void command_processing(struct param_type *params,
- enum modes *current_mode,
- struct w_queue *word_chain,
- struct c_queue *cmdlines,
- struct dynamic_array *tmp_word, int ch)
+ enum modes *current_mode,
+ struct w_queue *word_chain,
+ struct c_queue *cmdlines,
+ struct dynamic_array *tmp_word,
+ struct readline_type *readline, int ch)
{
char **cmdline = NULL;
+ int last_token = 0;
/* odd number of double quotes */
if(!is_double_quotes_pair(*params)) {
@@ -463,16 +545,22 @@ static void command_processing(struct param_type *params,
clean:
error_identification(params);
- if(params->tokens == '&' || params->tokens == 0 || ch == new_line)
+ last_token = params->tokens;
+ reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
+ readline);
+
+ if(last_token == '&' || last_token == 0 || ch == new_line) {
show_invitation();
- reset_params(params, current_mode, word_chain, cmdlines, tmp_word);
+ generate_readline(readline);
+ }
}
static void word_separation_processing(int ch, struct param_type *params,
enum modes *current_mode,
struct w_queue *word_chain,
struct c_queue *cmdlines,
- struct dynamic_array *tmp_word)
+ struct dynamic_array *tmp_word,
+ struct readline_type *readline)
{
/* could be a marker for the beginning of a blank word */
params->stored_symbol = ch;
@@ -492,16 +580,18 @@ static void word_separation_processing(int ch, struct param_type *params,
params->escape_sequences = 1;
return;
}
- if(special_token_handling(word_chain, cmdlines, tmp_word, ch, params)) {
+ if(special_token_handling(word_chain, cmdlines, tmp_word, ch, params,
+ readline)) {
if(command_execution_condition(params))
command_processing(params, current_mode, word_chain, cmdlines,
- tmp_word, ch);
+ tmp_word, readline, ch);
return;
}
if(params->wrong_command) {
error_identification(params);
clean_input_buffer();
- reset_params(params, current_mode, word_chain, cmdlines, tmp_word);
+ reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
+ readline);
show_invitation();
return;
}
@@ -509,7 +599,8 @@ static void word_separation_processing(int ch, struct param_type *params,
if(excessive_words(ch, params)) {
error_identification(params);
clean_input_buffer();
- reset_params(params, current_mode, word_chain, cmdlines, tmp_word);
+ reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
+ readline);
show_invitation();
return;
}
@@ -560,33 +651,69 @@ static void whole_word_processing(int ch, struct param_type *params,
add_letter(ch, tmp_word, params);
}
+static void change_terminal_settings(struct termios *cur_terminal_settings,
+ struct termios *save_terminal_settings)
+{
+ tcgetattr(0, save_terminal_settings);
+ memcpy(cur_terminal_settings, save_terminal_settings,
+ sizeof(*save_terminal_settings));
+ cur_terminal_settings->c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(0, TCSANOW, cur_terminal_settings);
+}
+
+static void restore_terminal_settings(struct termios *cur_terminal_settings,
+ struct termios *save_terminal_settings)
+{
+ tcsetattr(0, TCSANOW, save_terminal_settings);
+}
+
int main()
{
- int ch;
+ char ch;
+ int i;
struct param_type params;
+ struct termios cur_terminal_settings, save_terminal_settings;
struct w_queue word_chain;
struct c_queue cmdlines;
struct dynamic_array tmp_word;
+ struct readline_type readline;
enum modes current_mode = word_separation;
+ change_terminal_settings(&cur_terminal_settings, &save_terminal_settings);
w_queue_init(&word_chain);
c_queue_init(&cmdlines);
dynarr_create_array(&tmp_word);
+ readline_create_array(&readline);
init_params(&params, &current_mode);
show_invitation();
+ generate_readline(&readline);
+
+ for(i = 0; readline.arr[i] != end_of_file; ) {
+ ch = readline.arr[i];
+ readline.considered_index = i;
+
+ if(params.new_readline)
+ params.new_readline = 0;
- while((ch = getchar()) != EOF) {
if(ch == new_line)
command_processing(&params, &current_mode, &word_chain,
- &cmdlines, &tmp_word, ch);
- else if(current_mode == word_separation)
+ &cmdlines, &tmp_word, &readline, ch);
+ else if(current_mode == word_separation) {
word_separation_processing(ch, &params, &current_mode, &word_chain,
- &cmdlines, &tmp_word);
+ &cmdlines, &tmp_word, &readline);
+ /* double token was found */
+ if(readline.considered_index != i)
+ ++i;
+ }
else if(current_mode == whole_word)
whole_word_processing(ch, &params, &current_mode, &word_chain,
&tmp_word);
+ i = params.new_readline ? 0 : i + 1;
}
+
+ restore_terminal_settings(&cur_terminal_settings, &save_terminal_settings);
putchar(new_line);
dynarr_clear(&tmp_word);
+ readline_clear(&readline);
return 0;
}
diff --git a/shell.h b/shell.h
new file mode 100644
index 0000000..3fc11ad
--- /dev/null
+++ b/shell.h
@@ -0,0 +1,64 @@
+#ifndef SHELL_H_SENTRY
+#define SHELL_H_SENTRY
+
+#include "dynamic_array.h"
+#include "queue.h"
+
+enum {
+ /* CTRL-D */
+ end_of_file = 4,
+ new_line = 10,
+ whitespace = ' ',
+ tab = 9,
+ esc = 27,
+ backslash = '\\',
+ double_quotes = '"'
+};
+
+/* two-letter tokens */
+enum {
+ append = '>' + 1,
+ and = '&' + 1,
+ or = '|' + 1
+};
+
+/* error codes */
+enum {
+ err_filename_expected = 1,
+ err_redirect_stream_again = 2,
+ err_redirect_stream_in_pipeline = 3,
+ err_bg_process = 4,
+ err_empty_command = 5,
+ err_extra_chars_after_filename = 6,
+ err_odd_double_quotes = 7
+};
+
+/* 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 new_readline;
+};
+
+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);
+
+#endif