back to scratko.xyz
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-11-23 01:24:26 +0300
committerscratko <m@scratko.xyz>2024-11-23 01:24:26 +0300
commit3920a406c4161f6874d14ca8a78eca3c2b9fd9db (patch)
treeae4245f18ecd71c779c7d4cfbaec97db37725e61
parent821b146c54330cecba3ed2cc03a0f71ad83e540f (diff)
downloadshell-3920a406c4161f6874d14ca8a78eca3c2b9fd9db.tar.gz
shell-3920a406c4161f6874d14ca8a78eca3c2b9fd9db.tar.bz2
shell-3920a406c4161f6874d14ca8a78eca3c2b9fd9db.zip
Shell-VII releaseshell-VII
-rw-r--r--Makefile4
-rw-r--r--lexical_analysis.c152
-rw-r--r--lexical_analysis.h9
-rw-r--r--shell.c401
-rw-r--r--shell.h21
5 files changed, 483 insertions, 104 deletions
diff --git a/Makefile b/Makefile
index 9aa167e..691cedc 100644
--- a/Makefile
+++ b/Makefile
@@ -3,12 +3,12 @@ OBJMODULES = $(SRCMODULES:.c=.o)
CC = gcc
CFLAGS = -Wall -g -c
-all: shell-VI
+all: shell-VII
%.o: %.с %.h
$(CC) $(CFLAGS) $< -o $@
-shell-VI: $(OBJMODULES)
+shell-VII: $(OBJMODULES)
$(CC) $(LIBS) $^ -o $@
-include deps.mk
diff --git a/lexical_analysis.c b/lexical_analysis.c
index 5db57cb..eab4390 100644
--- a/lexical_analysis.c
+++ b/lexical_analysis.c
@@ -46,11 +46,39 @@ int is_empty_word(int ch, struct param_type params)
params.empty_word_flag;
}
-int command_execution_condition(struct param_type *params)
+static int find_end_subshell_before_cur_pos(struct readline_type *readline)
{
+ int i;
+ for(i = readline->considered_index-1; i >= 0; --i) {
+ if(readline->arr[i] == ' ')
+ continue;
+ break;
+ }
+ return i >= 0 && readline->arr[i] == ')';
+}
+
+int command_execution_condition(struct param_type *params,
+ struct readline_type *readline)
+{
+ if(params->tokens == and || params->tokens == or) {
+ if(find_end_subshell_before_cur_pos(readline))
+ return 0;
+ }
+ if(params->tokens == next_command) {
+ if(find_end_subshell_before_cur_pos(readline))
+ return 0;
+ }
+ if(params->tokens == '&') {
+ if(find_end_subshell_before_cur_pos(readline))
+ return 0;
+ }
+ if(params->tokens == '(' && params->pipeline)
+ return 1;
+
return
(params->tokens != '<' && params->tokens != '>' &&
- params->tokens != append && !params->pipeline) ||
+ params->tokens != append && !params->pipeline &&
+ params->tokens != start_subshell) ||
(params->pipeline && params->tokens == '&');
}
@@ -59,24 +87,24 @@ int is_first_special_token_character(int ch)
return ch == '<' || ch == '>' || ch == '&' || ch == '|' || ch == ';';
}
-int excessive_words(int ch, struct param_type *params)
+int excessive_words(int ch, struct param_type *params,
+ struct readline_type *readline)
{
- int next_ch;
-
if(filename_waiting(params)) {
if(ch == new_line)
return 0;
- while((next_ch = getchar()) != new_line) {
- if(next_ch == ' ')
- continue;
- if(!is_first_special_token_character(next_ch)) {
+ int i;
+ for(i = readline->considered_index+1; i <= readline->last_element_index;
+ ++i)
+ {
+ if(readline->arr[i] == ' ')
+ continue;
+ if(!is_first_special_token_character(readline->arr[i])) {
params->wrong_command = err_extra_chars_after_filename;
return 1;
- }
- else
+ } else
break;
}
- ungetc(next_ch, stdin);
}
return 0;
}
@@ -101,16 +129,10 @@ int validate_redirections(int ch, int next_ch, struct param_type *params)
params->streams.output_stream_to_append == NULL);
}
-int is_double_token(struct param_type *params)
-{
- return params->tokens == and || params->tokens == or ||
- params->tokens == append;
-}
-
int is_special_token(int ch, int next_ch)
{
- return (ch == '&' && next_ch == '&') || (ch == '|' && ch == '|') ||
- ch == '&' || ch == ';' || ch == '|';
+ return (ch == '&' && next_ch == '&') || (ch == '|' && next_ch == '|') ||
+ ch == '&' || ch == ';' || ch == '|' || ch == '(' || ch == ')';
}
int is_redirect_token(int ch, int next_ch)
@@ -139,8 +161,8 @@ 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))
- ++readline->considered_index;
+ if(params->tokens == append)
+ readline->considered_index += 2;
return 1;
} else
params->wrong_command = err_redirect_stream_again;
@@ -167,12 +189,20 @@ int pipeline_token_processing(struct w_queue *word_chain,
struct dynamic_array *tmp_word,
struct param_type *params)
{
+ /* pipeline after subshell */
+ if(tmp_word->last_element_index == -1 && params->tokens == start_subshell) {
+ params->tokens = '|';
+ params->pipeline = 1;
+ return 1;
+ }
+
char **cmdline = NULL;
if(is_stream_redirection_set(params) &&
wrong_streams_redirection(params)) {
params->wrong_command = err_redirect_stream_in_pipeline;
return 0;
}
+
params->tokens = '|';
params->pipeline = 1;
cmdline =
@@ -184,44 +214,88 @@ int pipeline_token_processing(struct w_queue *word_chain,
return 1;
}
+static void stop_by_previous_effect_tokens(struct param_type *params)
+{
+ if((params->tokens == and && last_execution_status == 1) ||
+ (params->tokens == or && last_execution_status == 0))
+ params->wrong_command = err_set_failure;
+}
+
+static int find_repeated_background_symbol(struct readline_type *readline)
+{
+ int i;
+ for(i = readline->considered_index + 1; i <= readline->last_element_index;
+ ++i)
+ {
+ if(readline->arr[i] == '&')
+ return 1;
+ }
+ return 0;
+}
+
/*
* verification of special tokens (|, &, &&, ||), except redirection tokens
*/
int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines,
- struct dynamic_array *tmp_word, int ch,
+ struct dynamic_array *tmp_word, int *ch,
struct param_type *params, struct readline_type *readline)
{
int next_ch, i;
-
- if(ch == '|' || ch == '&')
+ if(*ch == '|' || *ch == '&')
next_ch = readline->arr[readline->considered_index + 1];
- if(is_special_token(ch, next_ch)) {
+ if(is_special_token(*ch, next_ch)) {
add_word_or_filename(word_chain, tmp_word, params);
+ stop_by_previous_effect_tokens(params);
if(params->wrong_command)
return 0;
- if(ch == '|' && next_ch == '|')
+ 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 == '&')
+ readline->considered_index += 2;
+ return 1;
+ }
+ if(*ch == '|')
+ return pipeline_token_processing(word_chain, cmdlines, tmp_word,
+ params);
+ if(*ch == '&' && next_ch == '&') {
params->tokens = and;
- else if(ch == '&') {
+ readline->considered_index += 2;
+ return 1;
+ }
+ if(*ch == '&') {
+ /* repeated & */
+ if(find_repeated_background_symbol(readline)) {
+ params->wrong_command = err_repeated_background_ch;
+ return 0;
+ }
+
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;
- }
+ if(readline->arr[i] == ' ')
+ continue;
+ else
+ break;
}
+ if(readline->arr[i] == new_line)
+ *ch = new_line;
params->tokens = '&';
+ return 1;
+ }
+ if(*ch == start_subshell) {
+ params->tokens = start_subshell;
+ return 1;
+ }
+ if(*ch == end_subshell) {
+ params->tokens = end_subshell;
+ return 1;
}
- if(is_double_token(params))
- ++readline->considered_index;
+ if(*ch == next_command) {
+ params->tokens = next_command;
+ return 1;
+ }
+
return 1;
}
return 0;
diff --git a/lexical_analysis.h b/lexical_analysis.h
index 9d7d556..7fc1734 100644
--- a/lexical_analysis.h
+++ b/lexical_analysis.h
@@ -12,18 +12,19 @@ int ignore_spaces(int ch, struct param_type params);
int change_mode(int ch, struct param_type params);
int start_escape_sequence(int ch, struct param_type params);
int is_empty_word(int ch, struct param_type params);
-int command_execution_condition(struct param_type *params);
+int command_execution_condition(struct param_type *params,
+ struct readline_type *readline);
int is_special_token(int ch, int next_ch);
int is_redirect_token(int ch, int next_ch);
-int excessive_words(int ch, struct param_type *params);
+int excessive_words(int ch, struct param_type *params,
+ struct readline_type *readline);
void add_word_or_filename(struct w_queue *word_chain,
struct dynamic_array *tmp_word,
struct param_type *params);
int validate_redirections(int ch, int next_ch, struct param_type *params);
-int is_double_token(struct param_type *params);
int stream_redirect_tokens(struct w_queue *word_chain,
struct dynamic_array *tmp_word,
@@ -38,7 +39,7 @@ int pipeline_token_processing(struct w_queue *word_chain,
struct param_type *params);
int special_tokens(struct w_queue *word_chain, struct c_queue *cmdlines,
- struct dynamic_array *tmp_word, int ch,
+ struct dynamic_array *tmp_word, int *ch,
struct param_type *params, struct readline_type *readline);
#endif
diff --git a/shell.c b/shell.c
index 36eb916..be09e44 100644
--- a/shell.c
+++ b/shell.c
@@ -22,6 +22,7 @@ MAKE_QUEUE_PUSH(c_queue, cmdline, char**)
MAKE_QUEUE_PUSH(p_queue, pid, int)
enum modes { word_separation, whole_word };
+volatile sig_atomic_t last_execution_status = 0;
static void change_terminal_settings(struct termios *cur_terminal_settings,
struct termios *save_terminal_settings)
@@ -49,11 +50,17 @@ static void show_invitation()
static void handler(int signal)
{
int save_errno = errno;
- int pid;
+ int pid, result;
if(signal == SIGCHLD) {
do {
- pid = wait4(-1, NULL, WNOHANG, NULL);
+ pid = wait4(-1, &result, WNOHANG, NULL);
+ if(pid > 0) {
+ if(WIFEXITED(result))
+ last_execution_status = WEXITSTATUS(result) == 0 ? 0 : 1;
+ else
+ last_execution_status = 1;
+ }
} while(pid > 0);
}
errno = save_errno;
@@ -71,12 +78,17 @@ static void init_params(struct param_type *params, enum modes *current_mode)
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;
params->pipeline = 0;
params->new_readline = 1;
params->general_pgid = -1;
+ params->general_pipe_pgid = -1;
+ params->general_subshell_pgid = -1;
+ params->subshell = 0;
+ params->background_process = 0;
+ params->fd_read_for_channel = -1;
+ params->fd_read_for_subshell = -1;
*current_mode = word_separation;
+ last_execution_status = 0;
}
static void clear_filename(struct param_type *params)
@@ -105,6 +117,10 @@ static void reset_params(struct param_type *params,
readline_reset_array(readline);
clear_filename(params);
init_params(params, current_mode);
+ if(params->fd_read_for_channel != -1)
+ close(params->fd_read_for_channel);
+ if(params->fd_read_for_subshell != -1)
+ close(params->fd_read_for_subshell);
}
static void prepare_for_next_command_on_line(struct param_type *params,
@@ -149,6 +165,8 @@ static const char* error_code_to_token(int error_code)
case err_empty_command: return "empty command";
case err_extra_chars_after_filename: return "extra chars after filename";
case err_odd_double_quotes: return "odd number of double quotes";
+ case err_missing_closing_bracket: return "missing closing bracket";
+ case err_repeated_background_ch: return "repeated &";
}
return NULL;
}
@@ -160,24 +178,28 @@ 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 dynamic_array *tmp_word, int *ch,
struct param_type *params,
struct readline_type *readline)
{
return
- stream_redirect_tokens(word_chain, tmp_word, ch, params, readline) ?
+ 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)
{
- int total_process_counter, current_process_counter, wait_pid;
+ int total_process_counter, current_process_counter, wait_pid, result;
total_process_counter = p_queue_get_process_quantity(pid_store);
current_process_counter = 0;
do {
- wait_pid = wait(NULL);
+ wait_pid = wait(&result);
+ if(WIFEXITED(result))
+ last_execution_status = WEXITSTATUS(result) == 0 ? 0 : 1;
+ else
+ last_execution_status = 1;
if(p_queue_find_pid(pid_store, wait_pid))
++current_process_counter;
} while(total_process_counter != current_process_counter);
@@ -202,23 +224,34 @@ static void close_files(int input_fd, int output_fd)
close(output_fd);
}
+static int get_true_terminal_fd()
+{
+ if(isatty(0))
+ return 0;
+ else if(isatty(1))
+ return 1;
+ else return 2;
+}
+
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)
{
+ int terminal_fd;
clean_up_memory(word_chain, cmdlines, params);
close_files(input_fd, output_fd);
- if(params->tokens != '&') {
+ if(!params->background_process) {
wait_for_process_to_complete(pid_store);
signal(SIGTTOU, SIG_IGN);
- tcsetpgrp(0, getpid());
+ terminal_fd = get_true_terminal_fd();
+ tcsetpgrp(terminal_fd, getpid());
}
p_queue_clear(pid_store);
}
static void set_signal_disposition(struct param_type *params)
{
- if(params->tokens == '&')
+ if(params->background_process)
/* zombie process termination on signal */
signal(SIGCHLD, handler);
else
@@ -249,58 +282,146 @@ int is_stream_redirection_set(const struct param_type *params)
params->streams.output_stream_to_append;
}
+static void identify_general_pipe_pgid(struct param_type *params, int pid)
+{
+ int terminal_fd;
+ if(params->general_pipe_pgid == -1)
+ params->general_pipe_pgid = pid;
+ if(!params->background_process) {
+ /* foreground group */
+ terminal_fd = get_true_terminal_fd();
+ signal(SIGTTOU, SIG_IGN);
+ tcsetpgrp(terminal_fd, params->general_pipe_pgid);
+ }
+}
+
static void identify_general_pgid(struct param_type *params, int pid)
{
+ int terminal_fd;
if(params->general_pgid == -1)
params->general_pgid = pid;
- if(params->tokens != '&')
+ if(!params->background_process) {
/* foreground group */
- tcsetpgrp(0, params->general_pgid);
+ terminal_fd = get_true_terminal_fd();
+ signal(SIGTTOU, SIG_IGN);
+ tcsetpgrp(terminal_fd, params->general_pgid);
+ }
+}
+
+static int find_subshell_after_pipe(struct readline_type *readline)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index; ++i) {
+ if(readline->arr[i] == ' ')
+ continue;
+ if(readline->arr[i] == '(')
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+static int is_background_command(struct readline_type *readline)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index; ++i){
+ if(readline->arr[i] == '&' && readline->arr[i+1] != '&')
+ return 1;
+ else if(readline->arr[i] == '&' && readline->arr[i+1] == '&')
+ ++i;
+ }
+ return 0;
}
static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines,
struct param_type *params,
+ struct readline_type *readline,
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;
+ int subshell_fd[2];
+ int save_read_fd, pid, subshell_after;
- pipe(fd);
+ subshell_after = find_subshell_after_pipe(readline);
cmdline = c_queue_pop(cmdlines);
+
+ if(!c_queue_is_empty(cmdlines))
+ pipe(fd);
+
set_signal_disposition(params);
+
+#if 0
+ if(params->fd_pipe_rd != -1)
+ /* set foreground group by subshell process */
+ identify_general_pgid(params, params->general_pgid);
+#endif
+
+ if(c_queue_is_empty(cmdlines) && subshell_after)
+ pipe(subshell_fd);
+
pid = fork();
+#if 0
/* parent process */
if(pid)
identify_general_pgid(params, pid);
+#endif
+ if(pid) {
+ /* else set foreground group by first pipe process */
+ if(params->general_pipe_pgid == -1)
+ identify_general_pipe_pgid(params, pid);
+ }
if(pid == 0) {
- close(fd[0]);
- dup2(fd[1], 1);
- close(fd[1]);
- if(is_stream_redirection_set(params))
+ if(params->fd_read_for_channel != -1) {
+ dup2(params->fd_read_for_channel, 0);
+ close(params->fd_read_for_channel);
+ }
+ if(!c_queue_is_empty(cmdlines)) {
+ close(fd[0]);
+ dup2(fd[1], 1);
+ close(fd[1]);
+ } else if(subshell_after) {
+ close(subshell_fd[0]);
+ dup2(subshell_fd[1], 1);
+ close(subshell_fd[1]);
+ }
+ if(is_stream_redirection_set(params)) {
change_streams(input_fd, output_fd, 1, 1);
+ if(params->fd_read_for_channel != -1 && c_queue_is_empty(cmdlines))
+ change_streams(input_fd, output_fd, 1, 0);
+ }
/* change pgid */
- setpgid(getpid(), params->general_pgid == -1 ?
- getpid() : params->general_pgid);
+ setpgid(getpid(), params->general_pipe_pgid == -1 ? getpid() :
+ params->general_pipe_pgid);
execvp(cmdline[0], cmdline);
perror(cmdline[0]);
exit(1);
}
-
- close(fd[1]);
p_queue_push(&pid_store, pid);
+ if(params->fd_read_for_channel != -1)
+ close(params->fd_read_for_channel);
+
+ if(subshell_after && c_queue_is_empty(cmdlines)) {
+ params->fd_read_for_subshell = subshell_fd[0];
+ close(subshell_fd[1]);
+ }
+
+ if(!c_queue_is_empty(cmdlines))
+ close(fd[1]);
while(!c_queue_is_empty(cmdlines)) {
cmdline = c_queue_pop(cmdlines);
/* if not last process in pipeline*/
- if(!c_queue_is_empty(cmdlines)) {
+ if(!c_queue_is_empty(cmdlines)){
save_read_fd = fd[0];
pipe(fd);
- }
+ } else if(subshell_after)
+ pipe(subshell_fd);
+
pid = fork();
if(pid == 0) {
if(!c_queue_is_empty(cmdlines)) {
@@ -314,13 +435,17 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines,
close(fd[0]);
}
/* for last process in pipeline */
- if(c_queue_is_empty(cmdlines))
+ if(c_queue_is_empty(cmdlines)) {
if(is_stream_redirection_set(params))
change_streams(input_fd, output_fd, 1, 0);
-
+ if(subshell_after) {
+ close(subshell_fd[0]);
+ dup2(subshell_fd[1], 1);
+ close(subshell_fd[1]);
+ }
+ }
/* change pgid */
- setpgid(getpid(), params->general_pgid);
-
+ setpgid(getpid(), params->general_pipe_pgid);
execvp(cmdline[0], cmdline);
perror(cmdline[0]);
exit(1);
@@ -328,12 +453,23 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines,
if(!c_queue_is_empty(cmdlines)) {
close(save_read_fd);
close(fd[1]);
- } else
+ } else {
close(fd[0]);
+ if(subshell_after)
+ close(subshell_fd[1]);
+ }
p_queue_push(&pid_store, pid);
}
+ if(subshell_after)
+ params->fd_read_for_subshell = subshell_fd[0];
+ else
+ params->fd_read_for_subshell = -1;
postprocessing(word_chain, cmdlines, &pid_store, params, input_fd,
output_fd);
+#if 0
+ if(!subshell_after)
+ params->general_pgid = -1;
+#endif
}
static void open_files(const struct param_type *params, int *input_fd,
@@ -380,7 +516,8 @@ static void change_directory(char **cmdline)
static void run_external_program(struct w_queue *word_chain,
struct c_queue *cmdlines,
- struct param_type *params)
+ struct param_type *params,
+ struct readline_type *readline)
{
int pid, input_fd, output_fd;
struct p_queue pid_store;
@@ -389,6 +526,11 @@ static void run_external_program(struct w_queue *word_chain,
input_fd = 0;
output_fd = 0;
+ if(is_background_command(readline))
+ params->background_process = 1;
+ else
+ params->background_process = 0;
+
if(is_stream_redirection_set(params))
open_files(params, &input_fd, &output_fd);
@@ -397,7 +539,8 @@ static void run_external_program(struct w_queue *word_chain,
return;
}
if(params->pipeline) {
- make_pipeline(word_chain, cmdlines, params, input_fd, output_fd);
+ make_pipeline(word_chain, cmdlines, params, readline,
+ input_fd, output_fd);
return;
}
cmdline = c_queue_pop(cmdlines);
@@ -410,19 +553,29 @@ static void run_external_program(struct w_queue *word_chain,
perror("fork error");
exit(1);
}
+#if 0
/* parent process */
if(pid)
- identify_general_pgid(params, pid);
+ identify_general_pgid(params, getpid());
+#endif
+
/* child process */
if(pid == 0) {
if(is_stream_redirection_set(params))
change_streams(input_fd, output_fd, 0, 0);
- /* change pgid */
- setpgid(getpid(), params->general_pgid == -1 ?
- getpid() : params->general_pgid);
+ if(params->fd_read_for_channel != -1) {
+ dup2(params->fd_read_for_channel, 0);
+ close(params->fd_read_for_channel);
+ }
+ /* set foreground process */
+ if(params->subshell && params->general_pgid != -1)
+ setpgid(getpid(), params->general_pgid);
+ else {
+ identify_general_pgid(params, getpid());
+ setpgid(getpid(), getpid());
+ }
execvp(cmdline[0], cmdline);
- params->last_execution_status = 1;
perror(cmdline[0]);
exit(1);
}
@@ -430,6 +583,11 @@ static void run_external_program(struct w_queue *word_chain,
p_queue_push(&pid_store, pid);
postprocessing(word_chain, cmdlines, &pid_store, params, input_fd,
output_fd);
+ /* ??? */
+#if 0
+ if(!params->subshell)
+ params->general_pgid = -1;
+#endif
}
}
@@ -500,10 +658,14 @@ static void generate_readline(struct readline_type *readline)
{
struct termios cur_terminal_settings, save_terminal_settings;
enum keys found_key = none;
- int match_result;
+ int match_result, terminal_fd;
char ch;
change_terminal_settings(&cur_terminal_settings, &save_terminal_settings);
+ /* return foreground group by main process */
+ terminal_fd = get_true_terminal_fd();
+ signal(SIGTTOU, SIG_IGN);
+ tcsetpgrp(terminal_fd, getpid());
while((ch = getchar()) && !readline_termination_condition(ch, readline)) {
if(ch == tab) {
@@ -589,27 +751,41 @@ static void command_processing(struct param_type *params,
} else if(params->empty_word_flag || params->is_word)
add_word(word_chain, tmp_word, params);
- if((params->tokens == and || params->tokens == or) &&
- params->last_execution_status == 1) {
- /* ??? */
- fprintf(stderr, "cannot be performed\n");
- return;
+ if(word_chain->first == NULL && cmdlines->first == NULL) {
+ /* if not ending subshell */
+ if(params->tokens != '(')
+ params->wrong_command = err_empty_command;
+ goto clean;
}
-
- if(word_chain->first == NULL) {
- params->wrong_command = err_empty_command;
+#if 0
+ if((ch == new_line || ch == end_subshell) &&
+ ((params->tokens == and && last_execution_status == 1) ||
+ (params->tokens == or && last_execution_status == 0))) {
+ if(ch == end_subshell && params->subshell)
+ exit(1);
+ params->wrong_command = err_set_failure;
goto clean;
}
+#endif
+ /* not subshell process; was &&/|| command\n */
+ if(ch == new_line &&
+ ((params->tokens == and && last_execution_status == 1) ||
+ (params->tokens == or && last_execution_status == 0)))
+ 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);
+ if(word_chain->first != NULL) {
+ cmdline = create_cmdline(word_chain, w_queue_get_word_count(word_chain));
+ c_queue_push(cmdlines, cmdline);
+ }
+ run_external_program(word_chain, cmdlines, params, readline);
clean:
error_identification(params);
+ if(params->tokens == end_subshell && params->subshell)
+ exit(last_execution_status);
last_token = params->tokens;
- if(last_token == '&' || last_token == 0 || ch == new_line) {
+ if(last_token == 0 || ch == new_line) {
reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
readline);
show_invitation();
@@ -619,6 +795,99 @@ clean:
tmp_word);
}
+/* searching for the paired closed bracket too */
+static int find_pipeline_after_subshell(struct readline_type *readline,
+ struct param_type *params)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index &&
+ readline->arr[i] != ')'; ++i)
+ {}
+
+ if(i == readline->last_element_index+1) {
+ params->wrong_command = err_missing_closing_bracket;
+ return 0;
+ }
+ ++i;
+ while(i <= readline->last_element_index) {
+ if(readline->arr[i] == ' ') {
+ ++i;
+ continue;
+ }
+ break;
+ }
+ if(readline->arr[i] == '|')
+ return 1;
+ else
+ return 0;
+}
+
+static void readline_idx_after_subshell(struct readline_type *readline,
+ struct param_type *params)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index &&
+ readline->arr[i] != ')'; ++i)
+ {}
+
+ if(i == readline->last_element_index + 1) {
+ params->wrong_command = err_missing_closing_bracket;
+ return;
+ }
+
+ ++i;
+ /* skip whitespace symbols */
+ for( ; i <= readline->last_element_index && readline->arr[i] == ' '; ++i)
+ {}
+ readline->considered_index = i;
+}
+
+static void make_subshell(struct readline_type *readline,
+ struct param_type *params)
+{
+ int fd[2];
+ int pid, pipeline_after, pipeline_before;
+
+ pipeline_after = 0;
+
+ pipeline_after = find_pipeline_after_subshell(readline, params);
+ pipeline_before = params->fd_read_for_subshell != -1 ? 1 : 0;
+
+ if(pipeline_after) {
+ pipe(fd);
+ params->fd_read_for_channel = fd[0];
+ }
+
+ pid = fork();
+ /* child */
+ if(pid == 0) {
+ params->subshell = 1;
+ params->pipeline = 0;
+ if(pipeline_after) {
+ identify_general_pipe_pgid(params, getpid());
+ params->general_pgid = getpid();
+ setpgid(getpid(), params->general_pipe_pgid);
+ close(fd[0]);
+ dup2(fd[1], 1);
+ close(fd[1]);
+ }
+ if(pipeline_before) {
+ dup2(params->fd_read_for_subshell, 0);
+ close(params->fd_read_for_subshell);
+ params->general_pgid = params->general_pipe_pgid;
+ setpgid(getpid(), params->general_pipe_pgid);
+ }
+ /* parent */
+ } else {
+ sleep(1);
+ if(pipeline_after) {
+ close(fd[1]);
+ params->general_pipe_pgid = pid;
+ }
+ readline_idx_after_subshell(readline, params);
+ }
+}
+
static void word_separation_processing(int ch, struct param_type *params,
enum modes *current_mode,
struct w_queue *word_chain,
@@ -644,15 +913,22 @@ 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))
+ if(command_execution_condition(params, readline))
command_processing(params, current_mode, word_chain, cmdlines,
tmp_word, readline, ch);
+ if(params->tokens == start_subshell)
+ make_subshell(readline, params);
return;
}
if(params->wrong_command) {
error_identification(params);
+
+ /* subshell termination failed; failure with (&&, ||) too */
+ if(params->subshell)
+ exit(1);
+
clean_input_buffer();
reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
readline);
@@ -660,9 +936,12 @@ static void word_separation_processing(int ch, struct param_type *params,
return;
}
if(check_separation(ch, *params)) {
- if(excessive_words(ch, params)) {
+ if(excessive_words(ch, params, readline)) {
error_identification(params);
clean_input_buffer();
+ /* subshell termination failed */
+ if(params->subshell)
+ exit(1);
reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
readline);
show_invitation();
@@ -731,6 +1010,7 @@ int main()
dynarr_create_array(&tmp_word);
readline_create_array(&readline);
init_params(&params, &current_mode);
+ signal(SIGCHLD, handler);
show_invitation();
generate_readline(&readline);
@@ -747,9 +1027,18 @@ int main()
else if(current_mode == word_separation) {
word_separation_processing(ch, &params, &current_mode, &word_chain,
&cmdlines, &tmp_word, &readline);
- /* double token was found */
- if(readline.considered_index != i)
- ++i;
+ if(params.tokens == start_subshell && !params.subshell) {
+ i = readline.considered_index;
+ continue;
+ }
+ /*
+ * either a double token was found
+ * or the subshell part was skipped
+ */
+ if(readline.considered_index != i) {
+ i = readline.considered_index;
+ continue;
+ }
}
else if(current_mode == whole_word)
whole_word_processing(ch, &params, &current_mode, &word_chain,
diff --git a/shell.h b/shell.h
index b3dd93d..c92d68d 100644
--- a/shell.h
+++ b/shell.h
@@ -4,6 +4,8 @@
#include "dynamic_array.h"
#include "queue.h"
+#include <signal.h>
+
enum {
/* CTRL-D */
end_of_file = 4,
@@ -19,7 +21,10 @@ enum {
enum {
append = '>' + 1,
and = '&' + 1,
- or = '|' + 1
+ or = '|' + 1,
+ next_command = ';',
+ start_subshell = '(',
+ end_subshell = ')'
};
/* error codes */
@@ -30,7 +35,10 @@ enum {
err_bg_process = 4,
err_empty_command = 5,
err_extra_chars_after_filename = 6,
- err_odd_double_quotes = 7
+ err_odd_double_quotes = 7,
+ err_set_failure = 8,
+ err_missing_closing_bracket = 9,
+ err_repeated_background_ch = 10
};
/* storing file names to redirect standard input/output streams */
@@ -49,12 +57,19 @@ struct param_type {
int tokens;
int wrong_command;
struct io_type streams;
- int last_execution_status;
int pipeline;
int new_readline;
+ int general_pipe_pgid;
+ int general_subshell_pgid;
int general_pgid;
+ int subshell;
+ int fd_read_for_channel;
+ int fd_read_for_subshell;
+ int background_process;
};
+extern volatile sig_atomic_t last_execution_status;
+
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,