134 lines
3.4 KiB
C
134 lines
3.4 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stddef.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
|
||
|
#define MAX_SIZE 1024
|
||
|
|
||
|
// int LogCommandHistory(char* buf, char** command_log, int n) {
|
||
|
// command_log[n] = buf;
|
||
|
// }
|
||
|
|
||
|
void PrintCurrentDir() { // pwd
|
||
|
char cwd[MAX_SIZE];
|
||
|
if (getcwd(cwd, sizeof(cwd)) != NULL) {
|
||
|
printf("[%s] $ ", cwd);
|
||
|
} else {
|
||
|
perror("getcwd() error");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int ChangeDirectory(char** args) { // cd
|
||
|
args++; // Moving pointer to the next element (cd requires local or global path, the first argument is command name, so it's skipped)
|
||
|
char* path = args[0];
|
||
|
if (path == NULL || strcmp(path, "~") == 0) {
|
||
|
path = getenv("HOME");
|
||
|
}
|
||
|
if (chdir(path) != 0) {
|
||
|
perror("cd");
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void PrintHelp() { // help
|
||
|
printf("C Microshell implementation by Maciej Życzyński\n");
|
||
|
}
|
||
|
|
||
|
int HandleBuiltInCommands(char* command, char** args) { // Manual implementation of commands unsupported by execvp()
|
||
|
if (strcmp(command, "cd") == 0) {
|
||
|
ChangeDirectory(args);
|
||
|
return 1;
|
||
|
}
|
||
|
else if (strcmp(command, "help") == 0) {
|
||
|
PrintHelp();
|
||
|
return 1;
|
||
|
}
|
||
|
else if (strcmp(command, "exit") == 0) {
|
||
|
return 2;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int HandleCommand(char* command, char** args) { // Execute commands supported by execvp()
|
||
|
if (execvp(command, args) == -1) {
|
||
|
perror("Error executing command");
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int ParseCommand(char* buf, char** command, char** args) {
|
||
|
char buf_cp[MAX_SIZE];
|
||
|
strcpy(buf_cp, buf); // Leave the original buffer (important for command history implementation)
|
||
|
buf_cp[strlen(buf_cp) - 1] = '\0'; // Remove newline from input string
|
||
|
|
||
|
char* token = strtok(buf_cp, " ");
|
||
|
int tok_count = 0;
|
||
|
|
||
|
if (token == NULL) { // Retrieve command and arguments
|
||
|
return -1;
|
||
|
}
|
||
|
*command = token;
|
||
|
|
||
|
while (token != NULL) {
|
||
|
args[tok_count] = token;
|
||
|
tok_count++;
|
||
|
token = strtok(NULL, " ");
|
||
|
}
|
||
|
args[tok_count] = NULL; // Manually add NULL at the end (execvp() requires the argument array to be null-terminated)
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int main() {
|
||
|
char buf[MAX_SIZE];
|
||
|
char* command_log[MAX_SIZE];
|
||
|
int n = 0;
|
||
|
|
||
|
while (1) {
|
||
|
PrintCurrentDir();
|
||
|
|
||
|
char* command;
|
||
|
char* args[MAX_SIZE];
|
||
|
fgets(buf, sizeof(buf), stdin);
|
||
|
|
||
|
if (ParseCommand(buf, &command, args) < 0) {
|
||
|
printf("Invalid command\n");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// LogCommandHistory(buf, &command_log, n);
|
||
|
// n++;
|
||
|
|
||
|
int handler_value = HandleBuiltInCommands(command, args);
|
||
|
if (handler_value == 2) { // Exit command
|
||
|
break;
|
||
|
}
|
||
|
else if (handler_value == 0) { // Not a built-in command
|
||
|
|
||
|
// Forking logic
|
||
|
pid_t pid = fork();
|
||
|
if (pid < 0) {
|
||
|
perror("Error while forking\n");
|
||
|
}
|
||
|
else if (pid == 0) { // Child process
|
||
|
if (HandleCommand(command, args) == -1) {
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
exit(EXIT_SUCCESS);
|
||
|
}
|
||
|
else { // Parent process
|
||
|
int status;
|
||
|
if (waitpid(pid, &status, 0) == -1) {
|
||
|
perror("Error with parent process\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|