210 lines
5.0 KiB
C
210 lines
5.0 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 **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], "!")) {
|
|
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 print_ps() {
|
|
// TODO: add parsing config file
|
|
// FIXME: add proper PS1 (this is commented because of readline currently)
|
|
/*
|
|
if (!strcmp(pwd, homedir)) {
|
|
printf("%s@%s %s> ", username, hostname, "~");
|
|
} else {
|
|
printf("%s@%s %s> ", username, hostname, basename(pwd));
|
|
}*/
|
|
printf("orsh> ");
|
|
}
|
|
|
|
void signal_handler(int sig) {
|
|
if (pid == 0) {
|
|
printf("\n");
|
|
print_ps();
|
|
} else if (shell_pid == getpid()) { // Do nothing because readline prints the 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 {
|
|
while(1) {
|
|
line = readline("orsh> "); // FIXME: add prompt
|
|
if (line == NULL) {
|
|
printf("exit\n");
|
|
break;
|
|
}
|
|
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;
|
|
}
|