Files
tinysh/main.c
T

179 lines
4.2 KiB
C

#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
#include<string.h>
#include<readline/readline.h>
#include<readline/history.h>
#include<signal.h>
#include<errno.h>
#include"include/redirection.h"
pid_t backgroundPids[1024];
int backgroundCount = 0;
void handleSIGINT(int sig) {
const char *msg = "\n";
write(STDOUT_FILENO, msg, 1);
}
void handleSIGCHLD(int sig) {
int saved_errno = errno;
pid_t pid;
while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
for(int i = 0; i < 1024; i++) {
if(backgroundPids[i] == pid) {
backgroundPids[i] = 0;
}
}
}
errno = saved_errno;
}
int detectBackground(char **args) {
for (int i = 0; args[i] != NULL; i++) {
if (strcmp(args[i], "&") == 0) {
args[i] = NULL;
return 1;
}
}
return 0;
}
void executePipeline(char **args, int background) {
int num_cmds = 1;
for (int i = 0; args[i] != NULL; i++) {
if (strcmp(args[i], "|") == 0) num_cmds++;
}
char ***cmds = malloc(num_cmds * sizeof(char **));
int cmd_idx = 0;
cmds[cmd_idx++] = args;
for (int i = 0; args[i] != NULL; i++) {
if (strcmp(args[i], "|") == 0) {
args[i] = NULL;
cmds[cmd_idx++] = args + i + 1;
}
}
int (*pipes)[2] = malloc((num_cmds - 1) * sizeof(int[2]));
for (int i = 0; i < num_cmds - 1; i++) {
if (pipe(pipes[i]) < 0) {
perror("pipe");
free(cmds);
free(pipes);
return;
}
}
pid_t *pids = malloc(num_cmds * sizeof(pid_t));
for (int i = 0; i < num_cmds; i++) {
pids[i] = fork();
if (pids[i] == 0) {
signal(SIGINT, SIG_DFL);
if (i > 0) {
dup2(pipes[i - 1][0], STDIN_FILENO);
}
if (i < num_cmds - 1) {
dup2(pipes[i][1], STDOUT_FILENO);
}
for (int j = 0; j < num_cmds - 1; j++) {
close(pipes[j][0]);
close(pipes[j][1]);
}
Redirection in, out;
detectRedirections(cmds[i], &in, &out);
// Standard shell behavior: only first cmd gets <, only last gets >
if (i != 0) in.type = REDIRECT_NONE;
if (i != num_cmds - 1) out.type = REDIRECT_NONE;
if (applyRedirections(in, out) < 0) exit(1);
if (cmds[i][0] == NULL) exit(0);
execvp(cmds[i][0], cmds[i]);
perror("tinysh");
exit(1);
} else if (pids[i] < 0) {
perror("fork");
}
}
for (int i = 0; i < num_cmds - 1; i++) {
close(pipes[i][0]);
close(pipes[i][1]);
}
if (!background) {
for (int i = 0; i < num_cmds; i++) {
if (pids[i] > 0) waitpid(pids[i], NULL, 0);
}
} else {
for (int i = 0; i < num_cmds; i++) {
if (pids[i] > 0) {
if (backgroundCount < 1024) {
backgroundPids[backgroundCount++] = pids[i];
}
}
}
printf("{background} pipeline started\n");
}
free(cmds);
free(pipes);
free(pids);
}
int handleBuiltins(char **args) {
if(strcmp(args[0], "cd") == 0) {
chdir(args[1] == NULL ? getenv("HOME") : args[1]);
return 1;
}
else if(strcmp(args[0], "exit") == 0) {
exit(0);
} else { return 0;}
}
char **parse(char *line) {
char **args = malloc(256 * sizeof(char *));
if (!args) {
perror("malloc");
return NULL;
}
int i = 0;
char *token = strtok(line, " \t\n");
while(token != NULL && i < 255) {
args[i] = token;
i++;
token = strtok(NULL, " \t\n");
}
args[i] = NULL;
return args;
}
int main() {
signal(SIGINT, handleSIGINT);
signal(SIGCHLD, handleSIGCHLD);
while(1) {
char *user = getenv("USER");
char dir[1024];
getcwd(dir, sizeof(dir));
char host[1024];
gethostname(host, sizeof(host));
char prompt[2051];
char *home = getenv("HOME");
if(strncmp(dir, home, strlen(home)) == 0) {
snprintf(prompt, sizeof(prompt), "%s@%s ~%s$ ", user, host, dir + strlen(home));
} else {
snprintf(prompt, sizeof(prompt), "%s@%s %s$ ", user, host, dir);
}
char *line = readline(prompt);
if(line == NULL) return 0;
if(line && *line) add_history(line);
char **parsed = parse(line);
if(parsed[0] == NULL) { free(parsed); free(line); continue; }
if(handleBuiltins(parsed)) { free(line); free(parsed); continue; }
int background = detectBackground(parsed);
executePipeline(parsed, background);
free(parsed);
free(line);
}
}