- readline accepts stream to read from - fixed issues with EOF - arsh can be called with a file path as a second parameter, that will be interpreted
174 lines
3.1 KiB
C
174 lines
3.1 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <setjmp.h>
|
|
|
|
static sigjmp_buf restart_jmp_buf;
|
|
static const int restart_value = 69;
|
|
static volatile sig_atomic_t jump_active = 0;
|
|
|
|
void sigint_handler(int signo)
|
|
{
|
|
if(!jump_active)
|
|
{
|
|
return;
|
|
}
|
|
siglongjmp(restart_jmp_buf, restart_value);
|
|
}
|
|
static constexpr size_t max_line = 1024;
|
|
static char string_buffer[max_line];
|
|
char* readline(char* print, FILE* stream)
|
|
{
|
|
if(print)
|
|
{
|
|
printf("%s", print);
|
|
}
|
|
if(fgets(string_buffer, max_line, stream) == NULL)
|
|
{
|
|
if (feof(stdin))
|
|
{
|
|
return NULL; // Ctrl+D
|
|
}
|
|
if(ferror(stream))
|
|
{
|
|
perror("fgets error");
|
|
clearerr(stdin);
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
size_t length = strlen(string_buffer);
|
|
//Remove trailing \n
|
|
if (length > 0 && string_buffer[length - 1] == '\n')
|
|
{
|
|
string_buffer[length - 1] = '\0';
|
|
}
|
|
return string_buffer;
|
|
}
|
|
|
|
char** split_input(char* input)
|
|
{
|
|
const size_t alloc_size = ((strlen(input) + 1) / 2) + 1;
|
|
char** command = malloc(alloc_size * sizeof(char*));
|
|
if (command == NULL)
|
|
{
|
|
perror("malloc failed");
|
|
exit(1);
|
|
}
|
|
const char* separator = " ";
|
|
char* parsed;
|
|
uint32_t index = 0;
|
|
parsed = strtok(input, separator);
|
|
while (parsed != NULL)
|
|
{
|
|
command[index] = parsed;
|
|
index++;
|
|
parsed = strtok(NULL, separator);
|
|
}
|
|
command[index] = NULL;
|
|
return command;
|
|
}
|
|
|
|
int cd(char* path)
|
|
{
|
|
return chdir(path);
|
|
}
|
|
|
|
int main(int argc, const char* argv[])
|
|
{
|
|
FILE* stream = stdin;
|
|
char* prompt = "arsh> ";
|
|
if(argc == 2)
|
|
{
|
|
FILE* file = fopen(argv[1], "r");
|
|
if(file)
|
|
{
|
|
stream = file;
|
|
prompt = NULL;
|
|
}
|
|
}
|
|
|
|
struct sigaction s;
|
|
s.sa_handler = sigint_handler;
|
|
sigemptyset(&s.sa_mask);
|
|
s.sa_flags = SA_RESTART;
|
|
struct sigaction s_old;
|
|
sigaction(SIGINT, &s, &s_old);
|
|
|
|
int stat_loc;
|
|
|
|
if(sigsetjmp(restart_jmp_buf, 1) == restart_value)
|
|
{
|
|
printf("\n");
|
|
}
|
|
jump_active = 1;
|
|
|
|
while(true)
|
|
{
|
|
char* input = readline(prompt, stream);
|
|
if(input == NULL) //CTRL + D
|
|
{
|
|
if(prompt)
|
|
{
|
|
printf("\nexit\n");
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
// After that line you need cleanup
|
|
char** command = split_input(input);
|
|
if(command[0] == NULL)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
if(strlen(command[0]) >= 1 && command[0][0] == '#')
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
if(strcmp(command[0], "cd") == 0)
|
|
{
|
|
if (cd(command[1]) < 0)
|
|
{
|
|
perror(command[1]);
|
|
}
|
|
|
|
goto cleanup; //skip fork()
|
|
}
|
|
if(strcmp(command[0], "exit") == 0)
|
|
{
|
|
int32_t status_code = command[1] != NULL ? atoi(command[1]) : 0;
|
|
exit(status_code);
|
|
}
|
|
|
|
pid_t child_pid = fork();
|
|
if (child_pid < 0)
|
|
{
|
|
perror("Fork failed");
|
|
exit(1);
|
|
}
|
|
if (child_pid == 0)
|
|
{
|
|
sigaction(SIGINT, &s_old, NULL);
|
|
//never returns if call is successful
|
|
if(execvp(command[0], command) < 0)
|
|
{
|
|
perror(command[0]);
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
waitpid(child_pid, &stat_loc, WUNTRACED);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
free(command);
|
|
}
|
|
}
|