back to scratko.xyz
summaryrefslogtreecommitdiff
path: root/shell.c
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 /shell.c
parent8f4f87eabec13330a2b3a974975053c1e4632a11 (diff)
downloadshell-74c6a747a58b38131534556ab95fa4dd4b514780.tar.gz
shell-74c6a747a58b38131534556ab95fa4dd4b514780.tar.bz2
shell-74c6a747a58b38131534556ab95fa4dd4b514780.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).
Diffstat (limited to 'shell.c')
-rw-r--r--shell.c173
1 files changed, 150 insertions, 23 deletions
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;
}