//-----------------------------------------------------------------------------
// 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__
//-----------------------------------------------------------------------------
// function     fclose_proc
//
// purpose      Close a pipe to a process opened by fopen_proc.
//
// arguments    1 (FILE *) file descriptor to close
//
// returns      (int) final child process status
//-----------------------------------------------------------------------------
#define fclose_proc(f) fclose((f))

__FMACRO_END__

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	fopen_proc
//
// purpose	Create a pipe to a child process to read from its stdout (and
//		optionally stderr) or to write to its stdin, with the child
//		process running the given command and arguments.
//
// arguments	1 (const char *) option string
//		2 (const char *) file name to execute
//		3 (const char * const *) array of arguments
//
// returns	(FILE *) NULL : error
//		(FILE *) file pointer to pipe.
//
// options	"r" open to read from stdout (and stderr if not inherited)
//		"w" open to write to stdin
//		"i" inherit stdin from parent
//		"o" inherit stdout from parent
//		"e" inherit stderr from parent
//
// warning	Do not use pclose() on the stream returned by this function.
//		It works differently than popen() and is not compatible with
//		pclose().
//-----------------------------------------------------------------------------
FILE *
fopen_proc (
    const char *		arg_options
    ,
    const char *		arg_exec_name
    ,
    char * const *		arg_exec_args
    )
__PROTO_END__
{
    FILE *		file_p		;

    int *		fdi_p		;
    int *		fdo_p		;
    int *		fde_p		;

    const char *	opt		;
    const char *	ptr		;

    int			fdi		;
    int			fdo		;
    int			fde		;
    int			fd		;


    //-----------------
    // Check arguments.
    //-----------------
    if ( ! arg_options ) return NULL;
    if ( ! arg_exec_name || ! * arg_exec_name ) return NULL;
    if ( ! arg_exec_args || ! * arg_exec_args ) return NULL;

    //--------------------
    // Initialize options.
    //--------------------
    fdi_p = NULL;
    fdo_p = NULL;
    fde_p = NULL;
    fdi = -1;
    fdo = -1;
    fde = -1;
    opt = NULL;

    //--------------
    // Scan options.
    //--------------
    ptr = arg_options;
    while ( ( fd = * ptr ++ ) ) {
	if ( fd == 'r' ) {
	    opt = "rb";
	    fdo_p = & fd;
	    fde_p = & fd;
	}
	else if ( fd == 'w' ) {
	    opt = "wb";
	    fdi_p = & fd;
	}
	else if ( fd == 'i' ) {
	    fdi = 0;
	}
	else if ( fd == 'o' ) {
	    fdo = 1;
	}
	else if ( fd == 'e' ) {
	    fde = 2;
	}
    }

    //------------------
    // Check for errors.
    //------------------
    if ( ! opt ) return NULL;
    if ( fdi_p && fdo_p ) return NULL;
    if ( fdi >= 0 && fdi_p ) return NULL;
    if ( fdo >= 0 && fdo_p ) return NULL;
    if ( fde >= 0 ) fde_p = NULL;

    //--------------------------
    // Open a pipe to a process.
    //--------------------------
    fd = -1;
    if ( open_proc( fdi_p, fdo_p, fde_p, fdi, fdo, fde, -1, 1, arg_exec_name, arg_exec_args ) < 0 ) return NULL;
    if ( fd < 0 ) return NULL;

    //-----------------------------------------
    // Open a file pointer for this descriptor.
    //-----------------------------------------
    file_p = fdopen( fd, opt );
    if ( ! file_p ) close( fd );

    //---------------------------------------
    // Return the file pointer to the caller.
    //---------------------------------------
    return file_p;
}

