/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   e_externs_pipes.c                                  :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: rbousset <marvin@42.fr>                    +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2020/02/14 17:19:27 by rbousset          #+#    #+#             */
/*   Updated: 2020/02/14 17:19:29 by rbousset         ###   ########lyon.fr   */
/*                                                                            */
/* ************************************************************************** */

#include <sys/wait.h>
#include <libft.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "d_define.h"
#include "e_externs_next.h"
#include "f_fail.h"
#include "m_redirs.h"
#include "s_destroy.h"
#include "s_line.h"
#include "s_lpipes.h"
#include "s_struct.h"

static uint8_t
	get_builtin_id(const char com[],
				t_msh *msh)
{
	uint8_t	i;

	i = 0;
	while (msh->bu_ref[i] && ft_strncmp(com, msh->bu_ref[i],
		ft_strlen(msh->bu_ref[i]) + 1) != 0)
	{
		i++;
	}
	return (i);
}

static void
	e_extern_child(const char *fullpath,
					t_com *ptr,
					t_msh *msh)
{
	uint8_t	bu_id;
	uint8_t	ret;

	dup_redirs(ptr, msh);
	if (ft_strncmp(fullpath, "builtin", 8) == 0)
	{
		bu_id = get_builtin_id(ptr->bin, msh);
		ret = msh->bu_ptr[bu_id](ptr->argv + 1, msh);
		s_line_clear(&msh->curr);
		s_destroy(msh);
		exit(ret);
	}
	else
		execve(fullpath, ptr->argv, msh->envp);
	/* TODO: handle execve failed */
}

static size_t
	e_get_pipes_count(struct s_lpipes *ptr)
{
	struct s_lpipes	*rptr;
	size_t			pipes;

	rptr = ptr;
	pipes = 0;
	while (rptr->next != NULL)
	{
		rptr = rptr->next;
		pipes++;
	}
	return (pipes);
}

static void
	exec_path(const char *fullpath[],
				struct s_lpipes *head,
				t_msh *msh)
{
	size_t			pipes;
	size_t			i;
	size_t			j;
	int				fd[256][2];
	int				pid;
	int				status;

	/* TODO: norm, error mgmnt */
	pipes = e_get_pipes_count(head);
	i = 0;
	while (i < pipes)
	{
		pipe(fd[i]);
		i++;
	}
	i = 0;
	while (i <= pipes)
	{
		if ((pid = fork()) == 0)
		{
			if (i != 0)
				dup2(fd[i - 1][FT_WRITE_END], STDIN_FILENO);
			if (i != pipes)
				dup2(fd[i][FT_READ_END], STDOUT_FILENO);
			j = 0;
			while (j < pipes)
			{
				close(fd[j][FT_WRITE_END]);
				close(fd[j][FT_READ_END]);
				j++;
			}
			e_extern_child(fullpath[i], head->com, msh);
		}
		head = head->next;
		i++;
	}
	i = 0;
	while (i < pipes)
	{
		close(fd[i][FT_WRITE_END]);
		close(fd[i][FT_READ_END]);
		i++;
	}
	waitpid(pid, &status, 0);
	msh->ret = WEXITSTATUS(status);
}

void
	e_externs_pipes(struct s_lpipes *ptr,
					t_msh *msh)
{
	struct s_lpipes	*head;
	struct s_lpipes	*rptr;
	char			**envpath;
	char			**fullpath;
	size_t			i;
	size_t			pipes;
	uint8_t			bu_id;

	head = ptr;
	rptr = ptr;
	pipes = e_get_pipes_count(head);
	if (!(fullpath = (char **)malloc((pipes + 2) * sizeof(char *))))
		f_alloc_and_destroy_msh(msh);
	fullpath[pipes + 1] = NULL;
	i = 0;
	while (rptr != NULL)
	{
		if (ft_ischarset("/.", rptr->com->bin[0]))
		{
			if ((fullpath[i] = ft_strdup(rptr->com->bin)) == NULL)
				f_alloc_and_destroy_msh(msh);
		}
		else if ((envpath = get_env_path(msh)) != NULL)
		{
			if ((bu_id = get_builtin_id(rptr->com->bin, msh))
				< FT_BUILTINS_COUNT)
			{
				if ((fullpath[i] = ft_strdup("builtin")) == NULL)
					f_alloc_and_destroy_msh(msh);
			}
			else
				fullpath[i] = search_in_path(rptr->com->bin, envpath, msh);
			ft_delwords(envpath);
		}
		i++;
		rptr = rptr->next;
	}
	i = 0;
	exec_path((const char**)fullpath, head, msh);
	ft_delwords(fullpath);
}