2024-02-01 20:02:30 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
2024-02-02 17:52:21 +01:00
|
|
|
#include "constants.h"
|
2024-02-01 20:02:30 +01:00
|
|
|
#include "history.h"
|
2024-02-02 17:52:21 +01:00
|
|
|
#include "rush.h"
|
2024-02-01 20:02:30 +01:00
|
|
|
|
2024-02-08 20:49:11 +01:00
|
|
|
int execute(char **args);
|
|
|
|
|
2024-02-01 20:02:30 +01:00
|
|
|
// Function declarations for builtin commands
|
|
|
|
int cd(char **args);
|
|
|
|
int help(char **args);
|
|
|
|
int quit(char **args);
|
|
|
|
int history(char **args);
|
2024-02-02 17:52:21 +01:00
|
|
|
int export(char **args);
|
|
|
|
int source(char **args);
|
2024-02-01 20:02:30 +01:00
|
|
|
|
|
|
|
// List of builtin commands' names
|
|
|
|
char *builtin_cmds[] = {
|
|
|
|
"cd",
|
|
|
|
"help",
|
|
|
|
"exit",
|
2024-02-02 17:52:21 +01:00
|
|
|
"history",
|
|
|
|
"export",
|
|
|
|
"source"
|
2024-02-01 20:02:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
int (*builtin_func[]) (char **) = {
|
|
|
|
&cd,
|
|
|
|
&help,
|
|
|
|
&quit, // cant name it exit as it is taken
|
2024-02-02 17:52:21 +01:00
|
|
|
&history,
|
|
|
|
&export,
|
|
|
|
&source
|
2024-02-01 20:02:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// number of built in commands
|
|
|
|
int num_builtins() {
|
|
|
|
return sizeof(builtin_cmds) / sizeof(char *);
|
|
|
|
}
|
|
|
|
|
|
|
|
// change directory
|
|
|
|
int cd(char **args) {
|
|
|
|
if (args[1] == NULL) {
|
|
|
|
char *home = getenv("HOME");
|
2024-02-06 19:29:16 +01:00
|
|
|
if (home == NULL) {
|
|
|
|
fprintf(stderr, "rush: HOME environment variable is missing\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2024-02-01 20:02:30 +01:00
|
|
|
if (chdir(home) != 0) {
|
|
|
|
perror("rush");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (chdir(args[1]) != 0) {
|
|
|
|
perror("rush");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// show help menu
|
|
|
|
int help(char **args) {
|
|
|
|
printf("rush v0.01-alpha\n");
|
|
|
|
printf("Built in commands:\n");
|
|
|
|
|
|
|
|
for (int i = 0; i < num_builtins(); i++) {
|
|
|
|
printf(" %s\n", builtin_cmds[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Use 'man' to read manual of programs\n");
|
|
|
|
printf("Licensed under GPL v3\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int quit(char **args) {
|
|
|
|
return 0; // exit prompting loop, which also the shell
|
|
|
|
}
|
|
|
|
|
|
|
|
int history(char **args) {
|
2024-02-08 20:49:11 +01:00
|
|
|
char **history = get_all_history(true);
|
2024-02-01 20:02:30 +01:00
|
|
|
|
|
|
|
for (int i = 0; history[i] != NULL; ++i) {
|
|
|
|
printf("%s\n", history[i]);
|
|
|
|
free(history[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(history);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2024-02-02 17:52:21 +01:00
|
|
|
int export(char **args) {
|
|
|
|
args++; // skip the command
|
|
|
|
while (*args != NULL) {
|
|
|
|
char *variable = strtok(*args, "=\n");
|
|
|
|
char *value = strtok(NULL, "=\n");
|
|
|
|
if (variable != NULL && value != NULL) {
|
|
|
|
if (setenv(variable, value, 1) != 0) {
|
|
|
|
fprintf(stderr, "rush: Error setting environment variable\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("rush: [arg0: %s] [arg1: %s]", args[0], args[1]);
|
|
|
|
printf("rush: [Variable: %s] [Value: %s]\n", variable, value);
|
|
|
|
fprintf(stderr, "rush: Syntax error when setting environment variable\nUse \"export VARIABLE=VALUE\"\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
args++;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int source(char **args) {
|
|
|
|
if (args[1] == NULL) {
|
|
|
|
fprintf(stderr, "rush: not enough arguments\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *file = fopen(args[1], "r");
|
|
|
|
|
|
|
|
if (file == NULL) {
|
|
|
|
fprintf(stderr, "rush: no such file or directory '%s'\n", args[1]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char line[RL_BUFSIZE];
|
|
|
|
int status;
|
|
|
|
while (fgets(line, sizeof(line), file) != NULL) {
|
|
|
|
// Remove newline character if present
|
|
|
|
size_t len = strlen(line);
|
|
|
|
if (len > 0 && line[len - 1] == '\n') {
|
|
|
|
line[len - 1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
char **args = argsplit(line);
|
|
|
|
status = execute(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
return status; // Indicate success
|
|
|
|
}
|
|
|
|
|
2024-02-01 20:02:30 +01:00
|
|
|
bool is_builtin(char *command) {
|
|
|
|
for (int i = 0; i < num_builtins(); i++) {
|
|
|
|
if (strcmp(command, builtin_cmds[i]) == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// execute built in commands or launch commands and wait it to terminate, return 1 to keep shell running
|
|
|
|
int execute(char **args) {
|
|
|
|
if (args[0] == NULL) { // An empty command was entered.
|
|
|
|
return 1;
|
|
|
|
}
|
2024-02-08 13:31:57 +01:00
|
|
|
|
2024-02-08 20:49:11 +01:00
|
|
|
save_command_history(args); // save command to history file
|
|
|
|
|
2024-02-08 13:31:57 +01:00
|
|
|
// prioritize builtin commands
|
2024-02-01 20:02:30 +01:00
|
|
|
for (int i = 0; i < num_builtins(); i++) {
|
|
|
|
if (strcmp(args[0], builtin_cmds[i]) == 0) {
|
|
|
|
return (*builtin_func[i])(args);
|
|
|
|
}
|
|
|
|
}
|
2024-02-08 20:49:11 +01:00
|
|
|
|
|
|
|
int num_arg = 0;
|
|
|
|
|
|
|
|
while (*args != NULL) {
|
|
|
|
num_arg++; // count number of args
|
|
|
|
args++;
|
2024-02-08 13:31:57 +01:00
|
|
|
}
|
2024-02-08 20:49:11 +01:00
|
|
|
|
|
|
|
args -= num_arg;
|
|
|
|
|
2024-02-08 13:31:57 +01:00
|
|
|
pid_t pid;
|
2024-02-01 20:02:30 +01:00
|
|
|
|
|
|
|
int status;
|
2024-02-08 13:31:57 +01:00
|
|
|
if ((pid = fork()) == 0) {
|
2024-02-01 20:02:30 +01:00
|
|
|
// Child process
|
|
|
|
if (execvp(args[0], args) == -1) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
fprintf(stderr, "rush: command not found: %s\n", args[0]);
|
|
|
|
}
|
|
|
|
}
|
2024-02-06 19:29:16 +01:00
|
|
|
exit(EXIT_FAILURE); // exit the child
|
2024-02-01 20:02:30 +01:00
|
|
|
} else if (pid < 0) {
|
2024-02-06 19:29:16 +01:00
|
|
|
perror("fork failed");
|
2024-02-01 20:02:30 +01:00
|
|
|
} else {
|
|
|
|
// Parent process
|
2024-02-08 13:31:57 +01:00
|
|
|
do {
|
2024-02-06 19:58:47 +01:00
|
|
|
waitpid(pid, &status, WUNTRACED); // wait child to be exited to return to prompt
|
2024-02-08 13:31:57 +01:00
|
|
|
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
2024-02-01 20:02:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|