//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/io
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

// #include <libh/map.h>

#include "io_lib.h"

#ifndef DEV_NULL
#define DEV_NULL	"/dev/null"
#endif

extern char **environ;


__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	open_proc_arg
//
// purpose	Open pipe(s) to a new process running the given command and
//		arguments.  The pipe must be closed via close_proc.
//
// arguments	1 (int *) pointer to store parent end of pipe to stdin
//		2 (int *) pointer to store parent end of pipe to stdout
//		3 (int *) pointer to store parent end of pipe to stderr
//		4 (int) file descriptor to use if no pipe for stdin
//		5 (int) file descriptor to use if no pipe for stdout
//		6 (int) file descriptor to use if no pipe for stderr
//		7 (int) highest descriptor to be closed in child
//		8 (int) 1 if wait for child now.
//		9 (const char *) file name to execute
//		10-N (const char *) program arguments as function arguments
//
// returns	(int)  < 0 : error
//		(int) == 0 : OK, child already exited, grandchild running
//		(int)  > 0 : OK, PID of child running, must wait later
//
// note		If the pointers given for stdout and stderr are the same,
//		then a single pipe is created and the child will have both
//		stdout and stderr sharing the write end of that pipe.
//
// note		Caller should keep track of the highest file descriptor it
//		has opened and pass it via argument 7 to ensure that the
//		child process closes everything but standard descriptors.
//
// note		If the caller needs to pass other file descriptors to the
//		child, then this function is not appropriate for use.
//-----------------------------------------------------------------------------
#define open_proc_arg(a,b,c,d,e,f,g,h,i,j...) ((open_proc_arg)((a),(b),(c),(d),(e),(f),(g),(h),(i),j,(const char*)(NULL)))

__FMACRO_END__

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	open_proc_arg
//
// purpose	Open pipe(s) to a new process running the given command and
//		arguments.  The pipe must be closed via close_proc.
//
// arguments	1 (int *) pointer to store parent end of pipe to stdin
//		2 (int *) pointer to store parent end of pipe to stdout
//		3 (int *) pointer to store parent end of pipe to stderr
//		4 (int) file descriptor to use if no pipe for stdin
//		5 (int) file descriptor to use if no pipe for stdout
//		6 (int) file descriptor to use if no pipe for stderr
//		7 (int) highest descriptor to be closed in child
//		8 (int) 1 if wait for child now.
//		9 (const char *) file name to execute
//		10-N (const char *) program arguments as function arguments
//		N+1 (const char *) NULL to indicate end of arguments
//
// returns	(int)  < 0 : error
//		(int) == 0 : OK, child already exited, grandchild running
//		(int)  > 0 : OK, PID of child running, must wait later
//
// note		If the pointers given for stdout and stderr are the same,
//		then a single pipe is created and the child will have both
//		stdout and stderr sharing the write end of that pipe.
//
// note		Caller should keep track of the highest file descriptor it
//		has opened and pass it via argument 7 to ensure that the
//		child process closes everything but standard descriptors.
//
// note		If the caller needs to pass other file descriptors to the
//		child, then this function is not appropriate for use.
//-----------------------------------------------------------------------------
int
(open_proc_arg) (
    int *		arg_fd_ptr_stdin
    ,
    int *		arg_fd_ptr_stdout
    ,
    int *		arg_fd_ptr_stderr
    ,
    int			arg_inh_fd_stdin
    ,
    int			arg_inh_fd_stdout
    ,
    int			arg_inh_fd_stderr
    ,
    int			arg_fd_high
    ,
    int			arg_wait_now
    ,
    const char *	arg_exec_name
    ,
    ...
    )
__PROTO_END__
{
    char * *		args_array	;
    char * *		args_ptr	;
    va_list		var_args	;
    int			args_count	;

    //-----------------
    // Check arguments.
    //-----------------
    if ( ! arg_exec_name || ! * arg_exec_name ) return -1;

    //---------------------
    // Count the arguments.
    //---------------------
    args_count = 1;
    va_start( var_args, arg_exec_name );
    while ( va_arg( var_args, char * ) ) ++ args_count;
    va_end( var_args );

    //----------------------------------------------
    // Allocate a temporary array for the arguments.
    //----------------------------------------------
    args_array = alloca( sizeof (char *) * args_count );
    if ( ! args_array ) return -1;

    //--------------------
    // Populate the array.
    //--------------------
    args_ptr = args_array;
    va_start( var_args, arg_exec_name );
    while ( ( * args_ptr = va_arg( var_args, char * ) ) ) ++ args_ptr;
    va_end( var_args );

    //------------------------------------------
    // Let open_proc() do all the hard work.
    //------------------------------------------
    return open_proc( arg_fd_ptr_stdin,
		      arg_fd_ptr_stdout,
		      arg_fd_ptr_stderr,
		      arg_inh_fd_stdin,
		      arg_inh_fd_stdout,
		      arg_inh_fd_stderr,
		      arg_fd_high,
		      arg_wait_now,
		      arg_exec_name,
		      args_array
    );
}

