Last active
January 9, 2023 12:40
-
-
Save os-moussao/7a5931f85d23a88e5e570353024cec17 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file pipex.c | |
* @author @os-moussao | |
* @brief Implementing pipex project using a tree representation of the pipes | |
* @features input-output redirections, heredoc, supports multiple pipes | |
*/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <sys/wait.h> | |
#include <assert.h> | |
#define READ_END 0 | |
#define WRITE_END 1 | |
#define PP 1 | |
#define EX 2 | |
#define MAX_ARGS 6 | |
#define BUFFERSIZE 512 | |
typedef struct s_tree { | |
int type; | |
char **cmd; | |
struct s_tree *left; | |
struct s_tree *right; | |
} t_tree; | |
t_tree *new_tree(int type, char **cmd, t_tree *l, t_tree *r); | |
void disp_tree(t_tree *tree, int ident_level); | |
char **split_cmd(char *str); | |
int heredoc(char *delim); | |
// parse program arguments into a tree | |
t_tree *make_tree(char **av, int ac) { | |
t_tree *root = NULL; | |
root = new_tree(EX, split_cmd(av[0]), NULL, NULL); | |
for (int i = 1; i < ac; i++) { | |
root = new_tree(PP, NULL, root, new_tree(EX, split_cmd(av[i]), NULL, NULL)); | |
} | |
return root; | |
} | |
// recursively walk and execute the tree nodes | |
void run (t_tree *tree) { | |
if (!tree) | |
return ; | |
if (tree->type == EX) { | |
if (fork () == 0) { | |
// (execvp search for the right path of the command) | |
execvp(tree->cmd[0], tree->cmd); | |
} | |
wait(NULL); | |
} | |
else { | |
int p[2]; | |
pipe(p); | |
// run left node | |
if (fork() == 0) { | |
// redirect stdout | |
close(STDOUT_FILENO); | |
dup2(p[WRITE_END], STDOUT_FILENO); | |
close(p[READ_END]); | |
close(p[WRITE_END]); | |
run(tree->left); | |
return ; | |
} | |
// run right node | |
if (fork() == 0) { | |
// redirect stdin | |
close(STDIN_FILENO); | |
dup2(p[READ_END], STDIN_FILENO); | |
close(p[WRITE_END]); | |
close(p[READ_END]); | |
run(tree->right); | |
return ; | |
} | |
close(p[0]); | |
close(p[1]); | |
wait(NULL); | |
wait(NULL); | |
} | |
} | |
int main(int ac, char **av) | |
{ | |
if (ac < 4) { | |
printf("Usage: ./pipex (<infile> OR 'here_doc' <delimiter>) {<command>}1+ <outfile>\n"); | |
return 2; | |
} | |
// heredoc mode ?? | |
int hd_mode = (strcmp("<<", av[1]) == 0) || (strcmp("here_doc", av[1]) == 0); | |
if (hd_mode && ac < 5) { | |
printf("HEREDOC USAGE: ./pipex 'here_doc' <delimiter> {<command>}+ <outfile>\n"); | |
return 2; | |
} | |
int infile, outfile; | |
// open infile | |
if (hd_mode) | |
infile = heredoc(av[2]); | |
else | |
infile = open(av[1], O_RDONLY); | |
if (infile == -1) { | |
perror("open: infile"); | |
return 1; | |
} | |
outfile = open(av[ac - 1], O_WRONLY | O_TRUNC | O_CREAT, 0644); | |
if (outfile == -1) { | |
perror("open: outfile"); | |
return 1; | |
} | |
// redirect input | |
close(STDIN_FILENO); | |
dup2(infile, STDIN_FILENO); | |
close(infile); | |
// redirect output | |
close(STDOUT_FILENO); | |
dup2(outfile, STDOUT_FILENO); | |
close(outfile); | |
// make tree | |
t_tree *tree = make_tree(av + 2 + hd_mode, ac - 3 - hd_mode); | |
// display tree in stderr (for debugging) | |
fprintf(stderr, "\nParse tree:\n"); | |
disp_tree(tree, 0); | |
// run tree | |
run(tree); | |
exit(0); | |
} | |
// make heredoc | |
int heredoc(char *delim) { | |
int hfd[2], nbytes, len; | |
char buff[BUFFERSIZE + 1]; | |
if (pipe(hfd) == -1) { | |
return -1; | |
} | |
len = strlen(delim); | |
write(1, "heredoc> ", 9); | |
while ((nbytes = read(0, buff, BUFFERSIZE)) > 0) { | |
buff[nbytes] = 0; | |
if (nbytes == len + 1 && (memcmp(delim, buff, len) == 0) && buff[nbytes - 1] == '\n') { | |
break ; | |
} | |
write(hfd[WRITE_END], buff, nbytes); | |
write(1, "heredoc> ", 9); | |
} | |
close(hfd[WRITE_END]); | |
return (hfd[READ_END]); | |
} | |
// split command | |
char **split_cmd(char *str) { | |
int argc = 0; | |
char *token; | |
char **ret = malloc((MAX_ARGS + 1) * sizeof(char *)); | |
token = strtok(str, " "); | |
while (token) { | |
ret[argc++] = token; | |
if (argc == MAX_ARGS) // do not accept more than MAX_ARGS | |
break ; | |
token = strtok(NULL, " "); | |
} | |
ret[argc] = NULL; | |
return ret; | |
} | |
// constructor | |
t_tree *new_tree(int type, char **cmd, t_tree *l, t_tree *r) { | |
t_tree *tree = malloc(sizeof(t_tree)); | |
tree->type = type; | |
tree->cmd = cmd; | |
tree->left = l; | |
tree->right = r; | |
return tree; | |
} | |
// tree visualizer | |
void disp_tree(t_tree *tree, int ident_level) | |
{ | |
if (tree == NULL) | |
return ; | |
for (int i = 0; i < ident_level; i++) { | |
fprintf(stderr, "├ "); | |
} | |
if (tree->type == EX) | |
{ | |
fprintf(stderr, "├── EXEC: %s", tree->cmd[0]); | |
for (int i = 1; tree->cmd[i]; i++) | |
fprintf(stderr, " %s", tree->cmd[i]); | |
fprintf(stderr, "\n"); | |
} | |
else | |
fprintf(stderr, "├── PIPE:\n"); | |
disp_tree(tree->left, ident_level + 1); | |
disp_tree(tree->right, ident_level + 1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment