orsh/src/main.c

216 lines
5.1 KiB
C

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pwd.h>
#include <libgen.h>
#include <signal.h>
#include <readline/readline.h>
#include <readline/history.h>
int position = 0;
char pwd[128]; // FIXME: change to PATH_MAX
char hostname[1024]; // Hostname of machine
struct passwd *pw; // Struct of passwd for uid
char *username; // Current username
char *homedir; // HOME directory of current user
pid_t shell_pid; // pid of current shell (gets only on start)
pid_t pid; // FIXME: this shouldn't be global i think
char prompt[2048];
char **split_line(char *line, char *delim) {
int bufsize = 64;
position = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char *token;
if (!tokens) {
fprintf(stderr, "orsh: allocation error\n");
exit(EXIT_FAILURE);
}
token = strtok(line, delim);
while (token != NULL) {
tokens[position] = token;
position++;
token = strtok(NULL, delim);
}
tokens[position] = NULL;
return tokens;
}
int execute(char **command, int status) {
status = 0;
pid_t wpid;
pid = fork();
if (pid == 0) {
// Child process
if (execvp(command[0], command) == -1) {
perror("orsh");
}
status = 1; // FIXME: add error status
exit(EXIT_FAILURE);
} else if (pid < 0) {
// Error forking
perror("orsh");
status = 1; // FIXME: add error status
} else {
// Parent process
do {
wpid = waitpid(pid, &status, WUNTRACED); // FIXME: warning unused variable
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
if (status >= 256)
status = 1;
return status;
}
int parse_command(char **command, int status) {
int variable = 0;
int where_var[position - 1];
for (int i = 0; i <= position - 1; i++) { // Loop for checking if line has variable
if (command[i][0] == '$') { // FIXME
where_var[i] = 1;
variable++;
} else if (command[i][0] == '~') { // FIXME
command[i] = homedir;
}
}
if (variable >= 1) {
for (int i = 0; i <= position - 1; i++) {
if(where_var[i] == 1) {
command[i]++; // Deleting '$' char
if(!strcmp(command[i], "?")) {
char status_string[256]; // FIXME
sprintf(status_string, "%d", status);
command[i] = status_string;
} else {
command[i] = getenv(command[i]);
if (command[i] == NULL)
command[i] = "";
}
}
}
}
if (command[0][strlen(command[0]) - 1] == ';'){ // FIXME: parse all word not only first
command[0][strlen(command[0])-1] = '\0';
} else if (command[0][0] == ';'){ // Check if command is ; or first char of command is ;
command[0][0] = '\0';
}
if (status >= 256)
status = 1;
if (!strcmp(command[0], "exit")) { // FIXME: add switch statement (if possible)
printf("exit\n");
exit(0);
} else if (!strcmp(command[0], "echo")) {
for (int i = 1; i <= position - 1; i++)
printf("%s ", command[i]);
printf("\n");
return 0;
} else if (!strcmp(command[0], "cd")) {
if (position == 1) {
chdir(homedir);
getcwd(pwd, sizeof(pwd));
} else if (position >= 3) {
printf("orsh: cd: too many arguments\n");
} else {
chdir(command[1]);
getcwd(pwd, sizeof(pwd));
}
} else if (!strcmp(command[0], "pwd")) {
printf("%s\n", pwd);
} else if (!strcmp(command[0], "export")) {
for (int i = 1; i <= position - 1; i++) {
status = putenv(command[i]);
if (status != 0)
return status;
}
} else if (!strcmp(command[0], "!")) {
if (status != 0)
status = 0;
else if (status == 0)
status = 1;
} else {
status = execute(command, status);
}
return status;
}
void init() { // Some initial tasks
getcwd(pwd, sizeof(pwd)); // Get directory where shell was started
gethostname(hostname, sizeof(hostname)); // Get hostname
pw = getpwuid(getuid()); // Get passwd struct for current uid
username = pw->pw_name;
homedir = pw->pw_dir;
shell_pid = getpid();
}
void gen_prompt() {
// TODO: add parsing config file
if (!strcmp(pwd, homedir)) {
sprintf(prompt, "%s@%s %s> ", username, hostname, "~");
} else {
sprintf(prompt, "%s@%s %s> ", username, hostname, basename(pwd));
}
}
void signal_handler(int sig) {
if (pid == 0 || shell_pid == getpid()) {
printf("\n");
printf("%s", prompt);
} else
raise(sig);
}
int main(int argc, char *argv[]) {
char *line = NULL;
char script_line[100]; // FIXME
init();
char** commands;
char** args;
int status = 0; // FIXME
int command_status = 0; // FIXME
signal(SIGINT, signal_handler);
if (argc >= 2) {
FILE *script = fopen(argv[1], "r");
if (script == NULL) {
printf("Error opening file: %s\n", argv[1]); // FIXME: add proper error message
return 1;
}
while (fgets(script_line, 100, script) != NULL) {
args = split_line(script_line, " ");
command_status = parse_command(args, command_status);
}
} else {
using_history();
while(1) {
gen_prompt();
line = readline(prompt); // FIXME: add prompt
if (line == NULL) {
printf("exit\n");
break;
}
add_history(line);
commands = split_line(line, ";");
int after_parse_position = position; // To know where we in ; list FIXME
for (int i = 0; i <= after_parse_position - 1; i++) {
args = split_line(commands[i], " ");
command_status = parse_command(args, command_status);
}
}
}
return command_status;
}