Skip to content

Instantly share code, notes, and snippets.

@os-moussao
Last active January 9, 2023 12:40
Show Gist options
  • Save os-moussao/7a5931f85d23a88e5e570353024cec17 to your computer and use it in GitHub Desktop.
Save os-moussao/7a5931f85d23a88e5e570353024cec17 to your computer and use it in GitHub Desktop.
/**
* @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