back to scratko.xyz
summaryrefslogtreecommitdiff
path: root/shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell.c')
-rw-r--r--shell.c495
1 files changed, 345 insertions, 150 deletions
diff --git a/shell.c b/shell.c
index d3b96b3..131de99 100644
--- a/shell.c
+++ b/shell.c
@@ -1,5 +1,6 @@
#include "queue.h"
#include "dynamic_array.h"
+#include "lexical_analysis.h"
#include <stdio.h>
#include <stdlib.h>
@@ -10,7 +11,13 @@
#include <signal.h>
#include <errno.h>
+MAKE_QUEUE_INIT(w_queue)
+MAKE_QUEUE_INIT(p_queue)
+MAKE_QUEUE_PUSH(w_queue, word, char*)
+MAKE_QUEUE_PUSH(p_queue, pid, int)
+
enum modes { word_separation, whole_word };
+#if 0
enum {
new_line = 10,
whitespace = ' ',
@@ -43,7 +50,9 @@ struct param_type {
int wrong_command;
struct io_type streams;
int last_execution_status;
+ int pipeline;
};
+#endif
static void show_invitation()
{
@@ -83,6 +92,7 @@ static void init_params(struct param_type *params, enum modes *current_mode)
params->streams.output_stream_to_append = NULL;
/* 0 - success, 1 - error */
params->last_execution_status = 0;
+ params->pipeline = 0;
*current_mode = word_separation;
}
@@ -101,40 +111,39 @@ static void clear_filename(struct param_type *params)
static void reset_params(struct param_type *params,
enum modes *current_mode,
- struct queue *word_chain,
+ struct w_queue *word_chain,
+ struct c_queue *cmdlines,
struct dynamic_array *tmp_word)
{
- queue_clear(word_chain);
+ w_queue_clear(word_chain);
+ c_queue_clear(cmdlines);
dynarr_drop_word(tmp_word);
clear_filename(params);
init_params(params, current_mode);
}
-
+#if 0
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 is_stream_redirection_set(const struct param_type *params)
-{
- return params->streams.input_stream || params->streams.output_stream ||
- params->streams.output_stream_to_append;
-}
-
-static int filename_waiting(const struct param_type *params)
+#endif
+int filename_waiting(struct param_type *params)
{
- return params->tokens == '<' || params->tokens == '>' ||
- params->tokens == append;
+ if(params->tokens == '<' || params->tokens == '>' ||
+ params->tokens == append) {
+ if(!params->is_word)
+ params->wrong_command = 1;
+ return 1;
+ } else
+ return 0;
}
-static void add_filename(struct dynamic_array *tmp_word,
- struct param_type *params)
+void add_filename(struct dynamic_array *tmp_word, struct param_type *params)
{
dynarr_push_back(tmp_word, '\0');
switch(params->tokens) {
@@ -155,29 +164,35 @@ static void add_filename(struct dynamic_array *tmp_word,
params->is_word = 0;
}
-static void add_word(struct queue *word_chain,
- 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)
{
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);
+ w_queue_push(word_chain, word);
dynarr_drop_word(tmp_word);
params->is_word = 0;
}
+static void add_letter(int ch, struct dynamic_array *tmp_word,
+ struct param_type *params)
+{
+ dynarr_push_back(tmp_word, ch);
+ params->is_word = 1;
+ params->escape_sequences = 0;
+}
+
+#if 0
static int is_double_token(struct param_type *params)
{
return params->tokens == and || params->tokens == or ||
params->tokens == append;
}
-
static int is_redirect_token(int ch, int next_ch)
{
return ch == '<' || ch == '>' || (ch == '>' && next_ch == '>');
}
-
static int validate_redirections(int ch, int next_ch, struct param_type *params)
{
return (ch == '<' && params->streams.input_stream == NULL) ||
@@ -185,8 +200,19 @@ static int validate_redirections(int ch, int next_ch, struct param_type *params)
params->streams.output_stream == NULL &&
params->streams.output_stream_to_append == NULL);
}
+static void add_word_or_filename(struct w_queue *word_chain,
+ struct dynamic_array *tmp_word,
+ struct param_type *params)
+{
+ /* filenames */
+ if(filename_waiting(params) && !params->wrong_command)
+ add_filename(tmp_word, params);
+ /* execute command */
+ else if(params->is_word)
+ add_word(word_chain, tmp_word, params);
+}
-static int stream_redirect_tokens(struct queue *word_chain,
+static int stream_redirect_tokens(struct w_queue *word_chain,
struct dynamic_array *tmp_word, int ch,
struct param_type *params)
{
@@ -195,20 +221,10 @@ static int stream_redirect_tokens(struct queue *word_chain,
ungetc(next_ch, stdin);
if(is_redirect_token(ch, next_ch)) {
- /* filenames */
- if(filename_waiting(params))
- if(params->is_word)
- add_filename(tmp_word, params);
- else {
- fprintf(stderr, "syntax error\n");
- params->wrong_command = 1;
- return 0;
- }
- /* execute command */
- else if(params->is_word) {
- add_word(word_chain, tmp_word, params);
- params->is_word = 0;
- }
+ add_word_or_filename(word_chain, tmp_word, params);
+
+ if(params->wrong_command)
+ return 0;
if(validate_redirections(ch, next_ch, params)) {
params->tokens = (ch == '>' && next_ch == '>') ? append : ch;
@@ -222,6 +238,7 @@ static int stream_redirect_tokens(struct queue *word_chain,
}
return 0;
}
+#endif
static void clean_input_buffer()
{
@@ -229,67 +246,109 @@ static void clean_input_buffer()
while((ch = getchar()) != new_line)
{}
}
-
+#if 0
static int is_special_token(int ch)
{
return ch == and || ch == or || ch == '&' || ch == ';' || ch == '|';
}
+static int wrong_streams_redirection(struct param_type *params)
+{
+ return
+ (!params->pipeline && (params->tokens == '>' ||
+ params->tokens == append)) ||
+ (params->pipeline && (params->tokens == '>' ||
+ params->tokens == append ||
+ params->tokens == '<'));
+}
+#endif
+
+char** create_cmdline(const struct w_queue *word_chain, int word_counter)
+{
+ char **cmdline = malloc((word_counter + 1) * sizeof(char*));
+ w_queue_copy_words_to_args(word_chain, cmdline);
+ return cmdline;
+}
-static int special_tokens_allowed(int ch, struct param_type *params)
+int is_stream_redirection_set(const struct param_type *params)
{
- return (!filename_waiting(params) ||
- (filename_waiting(params) && params->is_word));
+ return params->streams.input_stream || params->streams.output_stream ||
+ params->streams.output_stream_to_append;
+}
+#if 0
+static int pipeline_token_processing(struct w_queue *word_chain,
+ struct c_queue *cmdlines,
+ struct dynamic_array *tmp_word,
+ struct param_type *params)
+{
+ char **cmdline = NULL;
+ if(is_stream_redirection_set(params) &&
+ wrong_streams_redirection(params)) {
+ /* TODO: add error codes */
+ params->wrong_command = 1;
+ return 0;
+ }
+ params->tokens = '|';
+ params->pipeline = 1;
+ cmdline =
+ create_cmdline(word_chain, w_queue_get_word_count(word_chain));
+
+ c_queue_push(cmdlines, cmdline);
+ w_queue_clear(word_chain);
+ dynarr_drop_word(tmp_word);
+ return 1;
}
-static int special_tokens(struct dynamic_array *tmp_word, int ch,
- struct param_type *params)
+static int special_tokens(struct w_queue *word_chain,
+ struct c_queue *cmdlines,
+ struct dynamic_array *tmp_word, int ch,
+ struct param_type *params)
{
int next_ch;
next_ch = getchar();
ungetc(next_ch, stdin);
if(is_special_token(ch)) {
- if(special_tokens_allowed(ch, params)) {
- if(filename_waiting(params) && params->is_word) {
- add_filename(tmp_word, params);
- params->is_word = 0;
- }
- if(ch == '|')
- params->tokens = '|';
- else if(ch == '|' && next_ch == '|')
- params->tokens = or;
- else if(ch == '&' && next_ch == '&')
- params->tokens = and;
- else if(ch == '&') {
- while((ch = getchar()) != new_line) {
- if(ch != whitespace && ch != tab) {
- fprintf(stderr, "incorrect command\n");
- params->wrong_command = 1;
- return 0;
- }
+ add_word_or_filename(word_chain, tmp_word, params);
+
+ if(params->wrong_command)
+ return 0;
+
+ if(ch == '|' && next_ch == '|')
+ params->tokens = or;
+ else if(ch == '|') {
+ if(!pipeline_token_processing(word_chain, cmdlines, tmp_word,
+ params))
+ return 0;
+ } else if(ch == '&' && next_ch == '&')
+ params->tokens = and;
+ else if(ch == '&') {
+ while((ch = getchar()) != new_line) {
+ if(ch != whitespace && ch != tab) {
+ fprintf(stderr, "incorrect command\n");
+ params->wrong_command = 1;
+ return 0;
}
- params->tokens = '&';
}
- if(is_double_token(params))
- getchar();
- return 1;
- } else {
- params->wrong_command = 1;
- fprintf(stderr, "filename expected\n");
+ params->tokens = '&';
}
+ if(is_double_token(params))
+ getchar();
+ return 1;
}
return 0;
}
+#endif
-static int special_token_handling(struct queue *word_chain,
- struct dynamic_array *tmp_word, int ch,
- 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)
{
return
stream_redirect_tokens(word_chain, tmp_word, ch, params) ?
- 1 : special_tokens(tmp_word, ch, params);
+ 1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params);
}
-
+#if 0
static int ignore_spaces(int ch, struct param_type params)
{
return (ch == whitespace || ch == tab) && !params.escape_sequences;
@@ -326,14 +385,7 @@ static int excessive_words(int ch, const struct param_type *params)
}
return 0;
}
-
-static char** create_cmdline(const struct queue *word_chain,
- int word_counter)
-{
- char **cmdline = malloc((word_counter + 1) * sizeof(char*));
- queue_copy_words_to_args(word_chain, cmdline);
- return cmdline;
-}
+#endif
static int is_cd_command(const char *arg)
{
@@ -364,14 +416,6 @@ static void change_directory(char **cmdline)
perror(path);
}
-static void clean_up_memory(struct queue *word_chain, char **cmdline,
- struct param_type *params)
-{
- queue_clear(word_chain);
- free(cmdline);
- clear_filename(params);
-}
-
static void open_files(const struct param_type *params, int *input_fd,
int *output_fd)
{
@@ -385,18 +429,57 @@ static void open_files(const struct param_type *params, int *input_fd,
O_APPEND, 0666);
}
-static void change_streams(int input_fd, int output_fd)
+static void change_streams(int input_fd, int output_fd, int is_pipeline,
+ int is_begin_pipeline)
{
- if(input_fd) {
+ if((!is_pipeline && input_fd) ||
+ (is_pipeline && is_begin_pipeline && input_fd)) {
dup2(input_fd, 0);
close(input_fd);
}
- if(output_fd) {
+ if((!is_pipeline && output_fd) ||
+ (is_pipeline && !is_begin_pipeline && output_fd)) {
dup2(output_fd, 1);
close(output_fd);
}
}
+static void set_signal_disposition(struct param_type *params)
+{
+ if(params->tokens == '&')
+ /* zombie process termination on signal */
+ signal(SIGCHLD, handler);
+ else
+ /* the parent process will wait for the process to complete;
+ default signal disposition
+ */
+ signal(SIGCHLD, SIG_DFL);
+}
+
+static void wait_for_process_to_complete(struct p_queue *pid_store)
+{
+ int total_process_counter, current_process_counter, wait_pid;
+ total_process_counter = p_queue_get_process_quantity(pid_store);
+ current_process_counter = 0;
+
+ do {
+ wait_pid = wait(NULL);
+ if(p_queue_find_pid(pid_store, wait_pid))
+ ++current_process_counter;
+ } while(total_process_counter != current_process_counter);
+
+ /* return of background process zombie cleanup */
+ signal(SIGCHLD, handler);
+}
+
+static void clean_up_memory(struct w_queue *word_chain,
+ struct c_queue *cmdlines, struct param_type *params)
+{
+ w_queue_clear(word_chain);
+ c_queue_clear(cmdlines);
+ clear_filename(params);
+}
+
static void close_files(int input_fd, int output_fd)
{
if(input_fd)
@@ -405,17 +488,94 @@ static void close_files(int input_fd, int output_fd)
close(output_fd);
}
-static void run_external_program(struct queue *word_chain,
+static void postprocessing(struct w_queue *word_chain, struct c_queue *cmdlines,
+ struct p_queue *pid_store, struct param_type *params,
+ int input_fd, int output_fd)
+{
+ clean_up_memory(word_chain, cmdlines, params);
+ close_files(input_fd, output_fd);
+ if(params->tokens != '&')
+ wait_for_process_to_complete(pid_store);
+ p_queue_clear(pid_store);
+}
+
+static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines,
+ struct param_type *params,
+ int input_fd, int output_fd)
+{
+ struct p_queue pid_store;
+ p_queue_init(&pid_store);
+ char **cmdline = NULL;
+ int fd[2];
+ int save_read_fd, pid;
+
+ pipe(fd);
+ cmdline = c_queue_pop(cmdlines);
+ set_signal_disposition(params);
+ pid = fork();
+
+ if(pid == 0) {
+ close(fd[0]);
+ dup2(fd[1], 1);
+ close(fd[1]);
+ if(is_stream_redirection_set(params))
+ change_streams(input_fd, output_fd, 1, 1);
+ execvp(cmdline[0], cmdline);
+ perror(cmdline[0]);
+ exit(1);
+ }
+
+ close(fd[1]);
+ p_queue_push(&pid_store, pid);
+
+ while(!c_queue_is_empty(cmdlines)) {
+ cmdline = c_queue_pop(cmdlines);
+ /* if not last process in pipeline*/
+ if(!c_queue_is_empty(cmdlines)) {
+ save_read_fd = fd[0];
+ pipe(fd);
+ }
+ pid = fork();
+ if(pid == 0) {
+ if(!c_queue_is_empty(cmdlines)) {
+ dup2(save_read_fd, 0);
+ close(save_read_fd);
+ dup2(fd[1], 1);
+ close(fd[1]);
+ } else {
+ dup2(fd[0], 0);
+ close(fd[0]);
+ }
+ /* for last process in pipeline */
+ if(c_queue_is_empty(cmdlines))
+ if(is_stream_redirection_set(params))
+ change_streams(input_fd, output_fd, 1, 0);
+ execvp(cmdline[0], cmdline);
+ perror(cmdline[0]);
+ exit(1);
+ }
+ if(!c_queue_is_empty(cmdlines)) {
+ close(save_read_fd);
+ close(fd[1]);
+ } else
+ close(fd[0]);
+ p_queue_push(&pid_store, pid);
+ }
+ postprocessing(word_chain, cmdlines, &pid_store, params, input_fd,
+ output_fd);
+}
+
+static void run_external_program(struct w_queue *word_chain,
+ struct c_queue *cmdlines,
struct param_type *params)
{
- int pid, wait_pid, result, input_fd, output_fd;
+ int pid, input_fd, output_fd;
+ struct p_queue pid_store;
+ p_queue_init(&pid_store);
+ char **cmdline = NULL;
input_fd = 0;
output_fd = 0;
- if(word_chain->first == NULL) {
- fprintf(stderr, "empty command\n");
- return;
- }
if(is_stream_redirection_set(params))
open_files(params, &input_fd, &output_fd);
@@ -423,100 +583,131 @@ static void run_external_program(struct queue *word_chain,
perror("can't open file");
return;
}
- char **cmdline = create_cmdline(word_chain,
- queue_get_word_count(word_chain));
- if(is_cd_command(cmdline[0])) {
- change_directory(cmdline);
- clean_up_memory(word_chain, cmdline, params);
+ if(params->pipeline) {
+ make_pipeline(word_chain, cmdlines, params, input_fd, output_fd);
+ return;
}
+ cmdline = c_queue_pop(cmdlines);
+ if(is_cd_command(cmdline[0]))
+ change_directory(cmdline);
else {
+ set_signal_disposition(params);
pid = fork();
if(pid == -1) {
perror("fork error");
exit(1);
}
- if(params->tokens == '&')
- /* zombie process termination on signal */
- signal(SIGCHLD, handler);
- else
- /* the parent process will wait for the process to complete;
- default signal disposition
- */
- signal(SIGCHLD, SIG_DFL);
-
/* child process */
if(pid == 0) {
if(is_stream_redirection_set(params))
- change_streams(input_fd, output_fd);
+ change_streams(input_fd, output_fd, 0, 0);
execvp(cmdline[0], cmdline);
params->last_execution_status = 1;
perror(cmdline[0]);
exit(1);
}
-
/* parent process */
- close_files(input_fd, output_fd);
- clean_up_memory(word_chain, cmdline, params);
-
- /* waiting for forground process by pid*/
- if(params->tokens != '&')
- do {
- wait_pid = wait(&result);
- } while(wait_pid != pid);
-
- /* return of background process zombie cleanup */
- signal(SIGCHLD, handler);
+ p_queue_push(&pid_store, pid);
+ postprocessing(word_chain, cmdlines, &pid_store, params, input_fd,
+ output_fd);
}
}
static void command_processing(struct param_type *params,
enum modes *current_mode,
- struct queue *word_chain,
+ struct w_queue *word_chain,
+ struct c_queue *cmdlines,
struct dynamic_array *tmp_word, int ch)
{
+ char **cmdline = NULL;
+
/* and odd number of double quotes */
if(!is_double_quotes_pair(*params)) {
print_error();
- reset_params(params, current_mode, word_chain, tmp_word);
+ reset_params(params, current_mode, word_chain, cmdlines, tmp_word);
show_invitation();
return;
}
+ if(filename_waiting(params)) {
+ if(params->wrong_command)
+ goto clean;
+ if(params->pipeline) {
+ if(params->tokens == '>' || params->tokens == append)
+ add_filename(tmp_word, params);
+ else if(params->tokens == '<') {
+ fprintf(stderr, "read in the last pipline element\n");
+ goto clean;
+ }
+ } else
+ add_filename(tmp_word, params);
+ } else if(params->empty_word_flag || params->is_word)
+ add_word(word_chain, tmp_word, params);
+#if 0
if(params->empty_word_flag || params->is_word) {
- if(filename_waiting(params))
+ if(filename_waiting(params)) {
if(excessive_words(ch, params)) {
fprintf(stderr, "too many args\n");
goto clean;
+ }
+ if(params->pipeline) {
+ if(params->tokens == '>' || params->tokens == append)
+ add_filename(tmp_word, params);
+ else if(params->tokens == '<') {
+ fprintf(stderr, "read in the last pipline element\n");
+ goto clean;
+ }
} else
add_filename(tmp_word, params);
- else
+ } else
add_word(word_chain, tmp_word, params);
+
} else if(filename_waiting(params)) {
fprintf(stderr, "filename expected\n");
goto clean;
}
+#endif
if((params->tokens == and || params->tokens == or) &&
- params->last_execution_status == 1)
+ params->last_execution_status == 1) {
fprintf(stderr, "cannot be performed\n");
- else
- run_external_program(word_chain, params);
+ return;
+ }
+
+ if(word_chain->first == NULL) {
+ fprintf(stderr, "empty command\n");
+ goto clean;
+ }
+
+ cmdline = create_cmdline(word_chain, w_queue_get_word_count(word_chain));
+ c_queue_push(cmdlines, cmdline);
+ run_external_program(word_chain, cmdlines, params);
clean:
if(params->tokens == '&' || params->tokens == 0 || ch == new_line)
show_invitation();
- reset_params(params, current_mode, word_chain, tmp_word);
+ reset_params(params, current_mode, word_chain, cmdlines, tmp_word);
}
+#if 0
static int is_empty_word(int ch, struct param_type params)
{
return (ch == whitespace || ch == tab) && !params.is_word &&
params.empty_word_flag;
}
+static int command_execution_condition(struct param_type *params)
+{
+ return
+ (!filename_waiting(params) && !params->pipeline) ||
+ (params->pipeline && params->tokens == '&');
+}
+#endif
+
static void word_separation_processing(int ch, struct param_type *params,
enum modes *current_mode,
- struct queue *word_chain,
+ struct w_queue *word_chain,
+ struct c_queue *cmdlines,
struct dynamic_array *tmp_word)
{
/* could be a marker for the beginning of a blank word */
@@ -537,21 +728,22 @@ static void word_separation_processing(int ch, struct param_type *params,
params->escape_sequences = 1;
return;
}
- if(special_token_handling(word_chain, tmp_word, ch, params)) {
- if(!filename_waiting(params))
- command_processing(params, current_mode, word_chain, tmp_word, ch);
+ if(special_token_handling(word_chain, cmdlines, tmp_word, ch, params)) {
+ if(command_execution_condition(params))
+ command_processing(params, current_mode, word_chain, cmdlines,
+ tmp_word, ch);
return;
}
if(params->wrong_command) {
clean_input_buffer();
- reset_params(params, current_mode, word_chain, tmp_word);
+ reset_params(params, current_mode, word_chain, cmdlines, tmp_word);
show_invitation();
return;
}
if(check_separation(ch, *params)) {
if(excessive_words(ch, params)) {
clean_input_buffer();
- reset_params(params, current_mode, word_chain, tmp_word);
+ reset_params(params, current_mode, word_chain, cmdlines, tmp_word);
fprintf(stderr, "too many args\n");
show_invitation();
return;
@@ -565,11 +757,10 @@ static void word_separation_processing(int ch, struct param_type *params,
if(ignore_spaces(ch, *params))
return;
- dynarr_push_back(tmp_word, ch);
- params->is_word = 1;
- params->escape_sequences = 0;
+ add_letter(ch, tmp_word, params);
}
+#if 0
static int escape_double_quotes_or_backslash(int ch, struct param_type params)
{
return params.escape_sequences &&
@@ -581,10 +772,11 @@ static int double_quotes_again(int ch, struct param_type params)
return ch == double_quotes && !params.is_word &&
params.stored_symbol == '"';
}
+#endif
static void whole_word_processing(int ch, struct param_type *params,
enum modes *current_mode,
- struct queue *word_chain,
+ struct w_queue *word_chain,
struct dynamic_array *tmp_word)
{
if(double_quotes_again(ch, *params)) {
@@ -613,29 +805,32 @@ static void whole_word_processing(int ch, struct param_type *params,
/* 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;
+
+ add_letter(ch, tmp_word, params);
}
int main()
{
int ch;
struct param_type params;
- struct queue word_chain;
+ struct w_queue word_chain;
+ struct c_queue cmdlines;
struct dynamic_array tmp_word;
enum modes current_mode = word_separation;
- queue_init(&word_chain);
+
+ w_queue_init(&word_chain);
+ c_queue_init(&cmdlines);
dynarr_create_array(&tmp_word);
init_params(&params, &current_mode);
show_invitation();
+
while((ch = getchar()) != EOF) {
if(ch == new_line)
command_processing(&params, &current_mode, &word_chain,
- &tmp_word, ch);
+ &cmdlines, &tmp_word, ch);
else if(current_mode == word_separation)
word_separation_processing(ch, &params, &current_mode, &word_chain,
- &tmp_word);
+ &cmdlines, &tmp_word);
else if(current_mode == whole_word)
whole_word_processing(ch, &params, &current_mode, &word_chain,
&tmp_word);