#include "shell.h" #include "queue.h" #include "dynamic_array.h" #include "lexical_analysis.h" #include "file_suggestions.h" #include "readline.h" #include #include #include #include #include #include #include #include #include MAKE_QUEUE_INIT(w_queue) MAKE_QUEUE_INIT(p_queue) MAKE_QUEUE_PUSH(w_queue, word, char*) 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) { 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); } static void show_invitation() { printf("> "); fflush(stdout); } /* remove background zombie processes */ static void handler(int signal) { int save_errno = errno; int pid, result; if(signal == SIGCHLD) { do { 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; } static void init_params(struct param_type *params, enum modes *current_mode) { params->is_word = 0; params->escape_sequences = 0; params->double_quotes_counter = 0; params->stored_symbol = ' '; 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; 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) { 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 w_queue *word_chain, struct c_queue *cmdlines, struct dynamic_array *tmp_word, struct readline_type *readline) { w_queue_clear(word_chain); c_queue_clear(cmdlines); dynarr_reset_array(tmp_word); 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, struct w_queue *word_chain, struct c_queue *cmdlines, struct dynamic_array *tmp_word) { w_queue_clear(word_chain); c_queue_clear(cmdlines); dynarr_reset_array(tmp_word); clear_filename(params); } 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; } static void clean_input_buffer() { int ch; while((ch = getchar()) != new_line) {} } static void print_error_msg(const char *error_msg) { if(error_msg) fprintf(stderr, "%s\n", error_msg); } static const char* error_code_to_token(int error_code) { switch(error_code) { case err_filename_expected: return "expected file name"; case err_redirect_stream_again: return "stream redirected again"; case err_redirect_stream_in_pipeline: return "bad stream redirect in pipe"; case err_bg_process: return "extra characters after &"; 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; } void error_identification(const struct param_type *params) { print_error_msg(error_code_to_token(params->wrong_command)); } 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 readline_type *readline) { return 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, result; total_process_counter = p_queue_get_process_quantity(pid_store); current_process_counter = 0; do { 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); /* 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) close(input_fd); if(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->background_process) { wait_for_process_to_complete(pid_store); signal(SIGTTOU, SIG_IGN); 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->background_process) /* 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 change_streams(int input_fd, int output_fd, int is_pipeline, int is_begin_pipeline) { if((!is_pipeline && input_fd) || (is_pipeline && is_begin_pipeline && input_fd)) { dup2(input_fd, 0); close(input_fd); } if((!is_pipeline && output_fd) || (is_pipeline && !is_begin_pipeline && output_fd)) { dup2(output_fd, 1); close(output_fd); } } 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 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->background_process) { /* foreground group */ 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 subshell_fd[2]; int save_read_fd, pid, subshell_after; 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) { 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_pipe_pgid == -1 ? getpid() : params->general_pipe_pgid); execvp(cmdline[0], cmdline); perror(cmdline[0]); exit(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)){ 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)) { dup2(save_read_fd, 0); close(save_read_fd); close(fd[0]); 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); if(subshell_after) { close(subshell_fd[0]); dup2(subshell_fd[1], 1); close(subshell_fd[1]); } } /* change pgid */ setpgid(getpid(), params->general_pipe_pgid); 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]); 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, 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 int is_cd_command(const char *arg) { return !strcmp(arg, "cd"); } static void change_directory(char **cmdline) { int result; char *first_arg = cmdline[1]; char *path = NULL; /* change to user home directory */ if(first_arg == NULL) { path = getenv("HOME"); if(!path) { perror("I don't know where's your home..."); return; } } else { if(cmdline[2]) { perror("cd: too many arguments"); return; } path = first_arg; } result = chdir(path); if(result == -1) perror(path); } static void run_external_program(struct w_queue *word_chain, struct c_queue *cmdlines, struct param_type *params, struct readline_type *readline) { 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(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); if(input_fd == -1 || output_fd == -1) { perror("can't open file"); return; } if(params->pipeline) { make_pipeline(word_chain, cmdlines, params, readline, 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 0 /* parent process */ if(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); 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); perror(cmdline[0]); exit(1); } /* parent process */ 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 } } int filename_waiting(struct param_type *params) { if(params->tokens == '<' || params->tokens == '>' || params->tokens == append) { if(!params->is_word) params->wrong_command = err_filename_expected; return 1; } else return 0; } 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_reset_array(tmp_word); params->is_word = 0; } 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); w_queue_push(word_chain, word); dynarr_reset_array(tmp_word); params->is_word = 0; } 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 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) { struct termios cur_terminal_settings, save_terminal_settings; enum keys found_key = none; 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) { 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); restore_terminal_settings(&cur_terminal_settings, &save_terminal_settings); } 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, struct readline_type *readline, int ch) { char **cmdline = NULL; int last_token = 0; /* odd number of double quotes */ if(!is_double_quotes_pair(*params)) { params->wrong_command = err_odd_double_quotes; goto clean; 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 == '<') { params->wrong_command = err_redirect_stream_in_pipeline; 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(word_chain->first == NULL && cmdlines->first == NULL) { /* if not ending subshell */ if(params->tokens != '(') params->wrong_command = err_empty_command; goto clean; } #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; 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 == 0 || ch == new_line) { reset_params(params, current_mode, word_chain, cmdlines, tmp_word, readline); show_invitation(); generate_readline(readline); } else prepare_for_next_command_on_line(params, word_chain, cmdlines, 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, struct c_queue *cmdlines, struct dynamic_array *tmp_word, struct readline_type *readline) { /* could be a marker for the beginning of a blank word */ params->stored_symbol = ch; if(is_empty_word(ch, *params)) { dynarr_push_back(tmp_word, '\0'); add_word(word_chain, tmp_word, params); params->empty_word_flag = 0; return; } params->empty_word_flag = 0; if(change_mode(ch, *params)) { ++params->double_quotes_counter; *current_mode = whole_word; return; } if(start_escape_sequence(ch, *params)) { params->escape_sequences = 1; return; } if(special_token_handling(word_chain, cmdlines, tmp_word, &ch, params, readline)) { 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); show_invitation(); return; } if(check_separation(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(); return; } if(!filename_waiting(params)) { add_word(word_chain, tmp_word, params); params->is_word = 0; } return; } if(ignore_spaces(ch, *params)) return; add_letter(ch, tmp_word, params); } static void whole_word_processing(int ch, struct param_type *params, enum modes *current_mode, struct w_queue *word_chain, struct dynamic_array *tmp_word) { if(double_quotes_again(ch, *params)) { params->stored_symbol = '\0'; params->empty_word_flag = 1; ++params->double_quotes_counter; *current_mode = word_separation; return; } params->stored_symbol = '\0'; if(change_mode(ch, *params)) { ++params->double_quotes_counter; *current_mode = word_separation; return; } if(escape_double_quotes_or_backslash(ch, *params)) { dynarr_push_back(tmp_word, ch); params->escape_sequences = 0; params->is_word = 1; return; } if(start_escape_sequence(ch, *params)) { params->escape_sequences = 1; return; } /* backslash recovery */ if(params->escape_sequences) dynarr_push_back(tmp_word, backslash); add_letter(ch, tmp_word, params); } int main() { char ch; int i; struct param_type params; struct w_queue word_chain; struct c_queue cmdlines; struct dynamic_array tmp_word; struct readline_type readline; enum modes current_mode = word_separation; w_queue_init(&word_chain); c_queue_init(&cmdlines); dynarr_create_array(&tmp_word); readline_create_array(&readline); init_params(¶ms, ¤t_mode); signal(SIGCHLD, handler); 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; if(ch == new_line) command_processing(¶ms, ¤t_mode, &word_chain, &cmdlines, &tmp_word, &readline, ch); else if(current_mode == word_separation) { word_separation_processing(ch, ¶ms, ¤t_mode, &word_chain, &cmdlines, &tmp_word, &readline); 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, ¶ms, ¤t_mode, &word_chain, &tmp_word); i = params.new_readline ? 0 : i + 1; } putchar(new_line); dynarr_clear(&tmp_word); readline_clear(&readline); return 0; }