#include "esh_main_loop.h" #include "esh_misc.h" #include "esh_history.h" #include "esh_init.h" void EShRunLoop() { struct termios orig_term_attr; struct termios new_term_attr; /* set the terminal to semi-raw mode */ tcgetattr(fileno(stdin), &orig_term_attr); memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios)); new_term_attr.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); new_term_attr.c_cc[VTIME] = 0; new_term_attr.c_cc[VMIN] = 1; tcsetattr(fileno(stdin), TCSANOW, &new_term_attr); char* command = malloc(esh_info_global->max_command_length); char* uncomplete_command = malloc(esh_info_global->max_command_length); // Used to restore after moves history for (;;) { EShShowMsg(); memset(command, 0, esh_info_global->max_command_length); int command_length = 0; int current_command_pos = 0; int current_history_step = 0; char input_char; memset(uncomplete_command, 0, esh_info_global->max_command_length); while (input_char = fgetc(stdin)) { if (input_char == 10) { printf("\n"); break; // 'Enter' pressed; } if (input_char == EOF) { continue; } if (input_char == '\033') { // Arrow up/down/left/right sequence fgetc(stdin); switch(fgetc(stdin)) { case 'A': { if (current_history_step == 0) { strcpy(uncomplete_command, command); } char* new_command = EShReceiveCommandFromHistory(current_history_step); if (new_command != NULL) { ++current_history_step; for (int i = current_command_pos; i < command_length; ++i) { printf(" "); ++current_command_pos; } for (int i = current_command_pos; i > 0; --i) { printf("\b \b"); } strcpy(command, new_command); command_length = strlen(new_command); current_command_pos = command_length; for (int i = 0; i < command_length; ++i) { printf("%c", command[i]); } } free(new_command); break; } case 'B': // Down { char* new_command; if (current_history_step > 0) { --current_history_step; } if (current_history_step > 0) { new_command = EShReceiveCommandFromHistory(current_history_step - 1); } else { new_command = uncomplete_command; } for (int i = current_command_pos; i < command_length; ++i) { printf(" "); ++current_command_pos; } for (int i = current_command_pos; i > 0; --i) { printf("\b \b"); } strcpy(command, new_command); command_length = strlen(new_command); current_command_pos = command_length; for (int i = 0; i < command_length; ++i) { printf("%c", command[i]); } if (current_history_step > 0) { free(new_command); } break; } case 'C': // Right if (current_command_pos < command_length) { printf("%c", command[current_command_pos]); ++current_command_pos; } break; case 'D': // Left if (current_command_pos > 0) { printf("\b"); --current_command_pos; } break; } continue; } if (input_char == 4) { // Ctrl + d printf("\n"); exit(0); } if (input_char == 127) { // Backspace if (current_command_pos > 0) { for (int i = current_command_pos - 1; i < command_length; ++i) { command[i] = command[i + 1]; } command[command_length - 1] = '\0'; printf("\b"); printf("%s", command + current_command_pos - 1); printf(" \b"); --current_command_pos; --command_length; for (int i = command_length - 1; i >= current_command_pos; --i) { printf("\b"); } } continue; } if (input_char == 3) { // Ctrl + c printf("\n"); exit(0); } if (EShIsShellLetter(input_char)) { for (int i = command_length; i >= current_command_pos; --i) { command[i + 1] = command[i]; } command[current_command_pos] = input_char; printf("%s", command + current_command_pos); ++current_command_pos; ++command_length; for (int i = command_length; i > current_command_pos; --i) { printf("\b"); } continue; } } if (command_length == 0) { printf("\n"); continue; } EShAddCommandToHistory(command); EShJob* jobs; int jobs_num = 0; jobs = EShParseCommandIntoJobs(command, &jobs_num); // EShPrintJobsDebugInfo(command, jobs, jobs_num); EShExecuteJobs(jobs_num, jobs); } /* restore the original terminal attributes */ tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr); } void EShShowMsg() { EShUpdateInviteMessage(); printf("%s", esh_info_global->invite_message); } EShJob* EShParseCommandIntoJobs(char* command, int* jobs_num) { EShJob* jobs = EShMakeJobsList(); *jobs_num = 1; int current_job_cmd_length = 0; for (int i = 0; i < strlen(command); ++i) { ESH_JOB_DELIMITER delimiter = EshIsJobDelimiter(command + i); if (delimiter != NOT_A_DELIMITER) { EShSplitJobCommand(&jobs[*jobs_num - 1], ' '); if (delimiter == SEMICOLON) { jobs[*jobs_num].job_start_condition = NO_CONDITION; } if (delimiter == LOGIC_AND) { jobs[*jobs_num].job_start_condition = PREVIOUS_EXIT_SUCCESS; } if (delimiter == LOGIC_OR) { jobs[*jobs_num].job_start_condition = PREVIOUS_EXIT_FAILED; } // TODO: delimiter == BIT_OR and BIT_AND ++(*jobs_num); current_job_cmd_length = 0; i += EShGetJobDelimiterSize(EshIsJobDelimiter(command + i)) - 1; continue; } jobs[*jobs_num - 1].command[current_job_cmd_length] = command[i]; ++current_job_cmd_length; } EShSplitJobCommand(&jobs[*jobs_num - 1], ' '); return jobs; } void EShSplitJobCommand(EShJob* job, char delim) { int token_status = 0; // 0 - token result[result_size] finished // 1 - token result[result_size] not finished int current_token_length = 0; for (int i = 0; i < strlen(job->command); ++i) { while (job->command[i] == delim && i < strlen(job->command)) { if (token_status == 1) { token_status = 0; } ++i; } if (i == strlen(job->command)) { if (token_status == 1) { ++job->command_tokens_num; } break; } if (token_status == 0) { token_status = 1; ++job->command_tokens_num; current_token_length = 0; } job->command_tokens[job->command_tokens_num - 1][current_token_length] = job->command[i]; ++current_token_length; } } void EShExecuteJobs(int jobs_num, EShJob* jobs_list) { for (int i = 0; i < jobs_num; ++i) { EShRunJob(&jobs_list[i]); } } void EShSetJobCommandType(EShJob* job) { } void EShRunJob(EShJob* job) { // TODO: PIPES INIT & BG PROCESS SUPPORT int pid = fork(); if (pid == 0) { for (int i = job->command_tokens_num; i < esh_info_global->max_command_tokens; ++i) { free(job->command_tokens[i]); job->command_tokens[i] = NULL; } execvp(job->command_tokens[0], job->command_tokens); printf("Esh: run command %s failed.\n", job->command); exit(1); // execlp failed } else { waitpid(pid, NULL, 0); printf("\n"); } } int EmptyCommand(const char* command) { } int InnerCommand(const char* command) { } void EShProcessInnerJob(const char* command) { } void EShProcessExecJob(const char* command) { } void EShPrintJobsDebugInfo(const char* command, EShJob* jobs, int jobs_num) { printf("\nD: Splitted command (%s) into %d jobs:\n", command, jobs_num); for (int i = 0; i < jobs_num; ++i) { printf("===================\n" "Job #%d:\n" "Cmd: %s\n" "Cmd tokens: %d ", i, jobs[i].command, jobs[i].command_tokens_num); for (int j = 0; j < jobs[i].command_tokens_num; ++j) { printf("{%s} ", jobs[i].command_tokens[j]); } printf("\n===================\n"); } printf("\n"); }