#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include "array.h"
#include "process.h"
// given a substring of a line tries to parse out as much information as possible
int parse_command(char const *line, char const *end, process *p) {
char *part;
char **local_parts;
if (arrayInit(part) != 0 || arrayInit(local_parts) != 0) {
fprintf(stderr, "Failed to prepare new part / parts array whilst parsing line");
return -1;
}
for (size_t i = 0; line + i < end; ++i) {
char c;
switch (c = line[i]) {
default:
arrayPush(part) = c;
break;
case ' ':
case '\t':
case '\0':
if (arrayLen(part) == 0)
continue;
arrayPush(part) = '\0';
arrayPush(local_parts) = part;
arrayInit(part);
}
}
if (arrayLen(part) == 0) {
arrayRelease(part);
} else {
arrayPush(part) = '\0';
arrayPush(local_parts) = part;
}
arrayPush(local_parts) = NULL;
p->argc = arrayLen(local_parts) - 1;
p->argv = local_parts;
return 0;
}
int parse_line(char const *const line, process **processes) {
// splits the line at | and then parses the commands
int ret_code;
if (arrayInit(*processes) != 0) {
perror("Failed to initialize processes array");
return -1;
}
bool done = false;
char const *cursor = line;
while (!done) {
process p = {.argv = NULL, .argc = 0, .in_fd = 0, .out_fd = 0, .pid = 0, .blocking = true};
char const *end = strchr(cursor, '|');
if (end == NULL) {
done = true;
end = line + strlen(line);
}
if ((ret_code = parse_command(cursor, end, &p)) != 0) {
return ret_code;
}
arrayPush(*processes) = p;
cursor = end + 1;
}
size_t p_len = arrayLen(*processes);
process *last = *processes + (p_len - 1);
// linking up all processes as we currently only have pipes for multiple commands
for (size_t i = 0; i < p_len - 1; ++i) {
int fds[2];
if (pipe(fds) != 0) {
perror("Failed to create pipe");
return -1;
}
(*processes)[i].out_fd = fds[1];
(*processes)[i + 1].in_fd = fds[0];
}
// setting all processes to non blocking when
if (strcmp(last->argv[last->argc - 1], "&") == 0) {
arrayPop(last->argv) = NULL;
last->argc = last->argc - 1;
last->argv[last->argc] = NULL;
for (size_t i = 0; i < p_len; ++i) {
(*processes)[i].blocking = false;
}
}
return ret_code;
}
int exec_command(process p) {
int status;
if ((p.pid = fork()) == 0) {
if (p.in_fd != 0) {
dup2(p.in_fd, 0);
close(p.in_fd);
}
if(p.in_fd == 0 && !p.blocking) {
close(0);
}
if (p.out_fd != 0) {
dup2(p.out_fd, 1);
close(p.out_fd);
}
execvp(p.argv[0], p.argv);
fprintf(stderr, "command not found: \"%s\"\n", p.argv[0]);
fflush(stderr);
exit(-1);
}
if (p.pid < 0) {
fprintf(stderr, "no fork\n");
exit(-2);
}
if (p.in_fd != 0) {
close(p.in_fd);
}
if (p.out_fd != 0) {
close(p.out_fd);
}
if (!p.blocking)
printf("[%i]\n", p.pid);
if (waitpid(p.pid, &status, p.blocking ? 0 : WNOHANG) < 0) {
fprintf(stderr, "wait error'ed out\n");
}
return WEXITSTATUS(status);
}
void free_processes(process **pr) {
process *processes = *pr;
while (!arrayIsEmpty(processes)) {
process p = arrayPop(processes);
while (!arrayIsEmpty(p.argv)) {
char *tmp = arrayTop(p.argv);
if (tmp) {
arrayRelease(tmp);
}
arrayPop(p.argv) = NULL;
}
arrayRelease(p.argv);
}
arrayRelease(processes);
*pr = NULL;
}