back to scratko.xyz
summaryrefslogtreecommitdiff
path: root/shell.c
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-06-03 14:40:07 +0300
committerscratko <m@scratko.xyz>2024-06-03 17:22:52 +0300
commitc9d02770f42339b6cc741191cee87e77b914b00b (patch)
treed2a1d913dc0a33324e6f9fb98bf89cc59470d14c /shell.c
parent859c929333b0c17f1b6027b17cc30bdf4763e6e2 (diff)
downloadshell-c9d02770f42339b6cc741191cee87e77b914b00b.tar.gz
shell-c9d02770f42339b6cc741191cee87e77b914b00b.tar.bz2
shell-c9d02770f42339b6cc741191cee87e77b914b00b.zip
Shell-IV release
Redirecting standard input-output streams.
Diffstat (limited to 'shell.c')
-rw-r--r--shell.c356
1 files changed, 300 insertions, 56 deletions
diff --git a/shell.c b/shell.c
index c03b62b..9ce5a32 100644
--- a/shell.c
+++ b/shell.c
@@ -1,10 +1,12 @@
#include "queue.h"
#include "dynamic_array.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
+#include <fcntl.h>
enum modes { word_separation, whole_word };
enum {
@@ -22,6 +24,13 @@ enum {
or = '|' + 1
};
+/* 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;
@@ -30,6 +39,8 @@ struct param_type {
int empty_word_flag;
int tokens;
int wrong_command;
+ struct io_type streams;
+ int last_execution_status;
};
static void show_invitation()
@@ -51,17 +62,36 @@ static void init_params(struct param_type *params, enum modes *current_mode)
params->empty_word_flag = 0;
params->tokens = 0;
params->wrong_command = 0;
+ params->streams.input_stream = NULL;
+ params->streams.output_stream = NULL;
+ params->streams.output_stream_to_append = NULL;
+ /* 0 - success, 1 - error */
+ params->last_execution_status = 0;
*current_mode = word_separation;
}
+static void clear_filename(struct param_type *params)
+{
+ if(params->streams.input_stream)
+ free(params->streams.input_stream);
+ if(params->streams.output_stream)
+ free(params->streams.output_stream);
+ if(params->streams.output_stream_to_append)
+ free(params->streams.output_stream_to_append);
+ params->streams.input_stream = NULL;
+ params->streams.output_stream = NULL;
+ params->streams.output_stream_to_append = NULL;
+}
+
static void reset_params(struct param_type *params,
- enum modes *current_mode,
- struct queue *word_chain,
- struct dynamic_array *tmp_word)
+ 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);
+ clear_filename(params);
+ init_params(params, current_mode);
}
static int is_double_quotes_pair(struct param_type params)
@@ -75,48 +105,173 @@ static int check_separation(int ch, struct param_type params)
!params.escape_sequences;
}
-static int single_tokens(int ch)
+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)
+{
+ return params->tokens == '<' || params->tokens == '>' ||
+ params->tokens == append;
+}
+
+static void add_filename(struct dynamic_array *tmp_word,
+ struct param_type *params)
+{
+ dynarr_push_back(tmp_word, '\0');
+ switch(params->tokens) {
+ case '<':
+ params->streams.input_stream = malloc(tmp_word->last_element_index+1);
+ dynarr_copy_array(tmp_word, params->streams.input_stream);
+ break;
+ case '>':
+ params->streams.output_stream = malloc(tmp_word->last_element_index+1);
+ dynarr_copy_array(tmp_word, params->streams.output_stream);
+ break;
+ case append:
+ params->streams.output_stream_to_append =
+ malloc(tmp_word->last_element_index+1);
+ dynarr_copy_array(tmp_word, params->streams.output_stream_to_append);
+ }
+ dynarr_drop_word(tmp_word);
+ params->is_word = 0;
+}
+
+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);
+ dynarr_drop_word(tmp_word);
+ params->is_word = 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 == '<' || ch == ';' || ch == '(' || ch == ')';
+ return (ch == '<' && params->streams.input_stream == NULL) ||
+ ((ch == '>' || (ch == '>' && next_ch == '>')) &&
+ params->streams.output_stream == NULL &&
+ params->streams.output_stream_to_append == NULL);
+}
+
+static int stream_redirect_tokens(struct queue *word_chain,
+ struct dynamic_array *tmp_word, int ch,
+ struct param_type *params)
+{
+ int next_ch;
+ next_ch = getchar();
+ 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;
+ }
+
+ if(validate_redirections(ch, next_ch, params)) {
+ params->tokens = (ch == '>' && next_ch == '>') ? append : ch;
+ if(is_double_token(params))
+ getchar();
+ return 1;
+ } else {
+ fprintf(stderr, "syntax error\n");
+ params->wrong_command = 1;
+ }
+ }
+ return 0;
}
static void clean_input_buffer()
{
int ch;
- while((ch = getchar() != new_line))
+ while((ch = getchar()) != new_line)
{}
}
-static void double_tokens_and_fg(int ch, struct param_type *params)
+static int is_special_token(int ch)
+{
+ return ch == and || ch == or || ch == '&' || ch == ';' || ch == '|';
+}
+
+static int special_tokens_allowed(int ch, struct param_type *params)
+{
+ return (!filename_waiting(params) ||
+ (filename_waiting(params) && params->is_word));
+}
+
+static int special_tokens(struct dynamic_array *tmp_word, int ch,
+ struct param_type *params)
{
int next_ch;
next_ch = getchar();
ungetc(next_ch, stdin);
- if(ch == '>' && next_ch == '>')
- params->tokens = append;
- 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;
+
+ 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;
+ }
+ }
+ params->tokens = '&';
}
+ if(is_double_token(params))
+ getchar();
+ return 1;
+ } else {
+ params->wrong_command = 1;
+ fprintf(stderr, "filename expected\n");
}
- params->tokens = '&';
}
+ return 0;
}
-static int check_tokens(int ch, struct param_type *params)
+static int special_token_handling(struct queue *word_chain,
+ struct dynamic_array *tmp_word, int ch,
+ struct param_type *params)
{
- if(single_tokens(ch))
- params->tokens = ch;
- else
- double_tokens_and_fg(ch, params);
- return params->tokens;
+ return
+ stream_redirect_tokens(word_chain, tmp_word, ch, params) ?
+ 1 : special_tokens(tmp_word, ch, params);
}
static int ignore_spaces(int ch, struct param_type params)
@@ -134,16 +289,35 @@ 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)
+/* name of file for opening stream already exists */
+
+static int excessive_words(int ch, const 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);
- dynarr_drop_word(tmp_word);
- params->is_word = 0;
+ int next_ch;
+#if 0
+ if(filename_waiting(params))
+ return
+ (params->tokens == '<' && params->streams.input_stream != NULL) ||
+ (params->tokens == '>' && params->streams.output_stream !=NULL) ||
+ (params->tokens == append &&
+ params->streams.output_stream_to_append != NULL);
+ else
+ return 0;
+#endif
+ if(filename_waiting(params)) {
+ if(ch == new_line)
+ return 0;
+ while((next_ch = getchar()) != new_line) {
+ if(next_ch == ' ')
+ continue;
+ if(!is_special_token(next_ch) && next_ch != '<' && next_ch != '>')
+ return 1;
+ else
+ break;
+ }
+ ungetc(next_ch, stdin);
+ }
+ return 0;
}
#if 0
@@ -171,7 +345,7 @@ static char** create_cmdline(const struct queue *word_chain,
return cmdline;
}
-static int check_cd(const char *arg)
+static int is_cd_command(const char *arg)
{
return !strcmp(arg, "cd");
}
@@ -200,10 +374,12 @@ static void change_directory(char **cmdline)
perror(path);
}
-static void clean_up_memory(struct queue *word_chain, char **cmdline)
+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 clean_up_zombie_process()
@@ -214,19 +390,60 @@ static void clean_up_zombie_process()
} while(pid > 0);
}
+static void open_files(const struct param_type *params, int *input_fd,
+ int *output_fd)
+{
+ if(params->streams.input_stream)
+ *input_fd = open(params->streams.input_stream, O_RDONLY);
+ if(params->streams.output_stream)
+ *output_fd = open(params->streams.output_stream, O_WRONLY | O_CREAT |
+ O_TRUNC, 0666);
+ if(params->streams.output_stream_to_append)
+ *output_fd = open(params->streams.output_stream_to_append, O_WRONLY |
+ O_APPEND, 0666);
+}
+
+static void change_streams(int input_fd, int output_fd)
+{
+ if(input_fd) {
+ dup2(input_fd, 0);
+ close(input_fd);
+ }
+ if(output_fd) {
+ dup2(output_fd, 1);
+ close(output_fd);
+ }
+}
+
+static void close_files(int input_fd, int output_fd)
+{
+ if(input_fd)
+ close(input_fd);
+ if(output_fd)
+ close(output_fd);
+}
+
static void run_external_program(struct queue *word_chain,
struct param_type *params)
{
- int pid, wait_pid, result;
+ int pid, wait_pid, result, input_fd, output_fd;
if(word_chain->first == NULL) {
fprintf(stderr, "empty command\n");
return;
}
+
+ if(is_stream_redirection_set(params))
+ open_files(params, &input_fd, &output_fd);
+
+ if(input_fd == -1 || output_fd == -1) {
+ perror("can't open file");
+ return;
+ }
char **cmdline = create_cmdline(word_chain,
queue_get_word_count(word_chain));
- if(check_cd(cmdline[0])) {
+ if(is_cd_command(cmdline[0])) {
change_directory(cmdline);
- clean_up_memory(word_chain, cmdline);
+ clean_up_memory(word_chain, cmdline, params);
}
else {
pid = fork();
@@ -236,13 +453,17 @@ static void run_external_program(struct queue *word_chain,
}
/* child process */
if(pid == 0) {
+ if(is_stream_redirection_set(params))
+ change_streams(input_fd, output_fd);
execvp(cmdline[0], cmdline);
+ params->last_execution_status = 1;
perror(cmdline[0]);
exit(1);
}
/* parent process */
- clean_up_memory(word_chain, cmdline);
+ close_files(input_fd, output_fd);
+ clean_up_memory(word_chain, cmdline, params);
/* waiting for forground process */
if(params->tokens != '&') {
@@ -257,7 +478,7 @@ static void run_external_program(struct queue *word_chain,
static void command_processing(struct param_type *params,
enum modes *current_mode,
struct queue *word_chain,
- struct dynamic_array *tmp_word)
+ struct dynamic_array *tmp_word, int ch)
{
/* and odd number of double quotes */
if(!is_double_quotes_pair(*params)) {
@@ -266,18 +487,31 @@ static void command_processing(struct param_type *params,
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);
+ if(filename_waiting(params))
+ if(excessive_words(ch, params)) {
+ fprintf(stderr, "too many args\n");
+ goto clean;
+ } else
+ add_filename(tmp_word, params);
+ else
+ add_word(word_chain, tmp_word, params);
+ } else if(filename_waiting(params)) {
+ fprintf(stderr, "filename expected\n");
+ goto clean;
}
- run_external_program(word_chain, params);
- if(params->tokens == '&' || params->tokens == 0)
+
+ if((params->tokens == and || params->tokens == or) &&
+ params->last_execution_status == 1)
+ fprintf(stderr, "cannot be performed\n");
+ else
+ run_external_program(word_chain, params);
+
+clean:
+ if(params->tokens == '&' || params->tokens == 0 || ch == new_line)
show_invitation();
reset_params(params, current_mode, word_chain, tmp_word);
-#if 0
- print_line(word_chain);
-#endif
}
static int is_empty_word(int ch, struct param_type params)
@@ -309,8 +543,9 @@ static void word_separation_processing(int ch, struct param_type *params,
params->escape_sequences = 1;
return;
}
- if(check_tokens(ch, params)) {
- command_processing(params, current_mode, word_chain, tmp_word);
+ if(special_token_handling(word_chain, tmp_word, ch, params)) {
+ if(!filename_waiting(params))
+ command_processing(params, current_mode, word_chain, tmp_word, ch);
return;
}
if(params->wrong_command) {
@@ -320,8 +555,17 @@ static void word_separation_processing(int ch, struct param_type *params,
return;
}
if(check_separation(ch, *params)) {
- add_word(word_chain, tmp_word, params);
- params->is_word = 0;
+ if(excessive_words(ch, params)) {
+ clean_input_buffer();
+ reset_params(params, current_mode, word_chain, tmp_word);
+ fprintf(stderr, "too many args\n");
+ show_invitation();
+ return;
+ }
+ if(!filename_waiting(params)) {
+ add_word(word_chain, tmp_word, params);
+ params->is_word = 0;
+ }
return;
}
if(ignore_spaces(ch, *params))
@@ -395,7 +639,7 @@ int main()
clean_up_zombie_process();
if(ch == new_line)
command_processing(&params, &current_mode, &word_chain,
- &tmp_word);
+ &tmp_word, ch);
else if(current_mode == word_separation)
word_separation_processing(ch, &params, &current_mode, &word_chain,
&tmp_word);