/*
 * Copyright © 2001 Novell, Inc. All Rights Reserved.
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Artistic License, as specified in the README file.
 *
 */

/*
 * FILENAME		:	NWPipe.c
 * DESCRIPTION	:	Functions to implement pipes on NetWare.
 * Author		:	HYAK
 * Date			:	January 2001.
 *
 */



#include <nwadv.h>
#include <nwdsdefs.h>

#include "win32ish.h"
#include "nwpipe.h"
#include "nwplglob.h"


// This was added since the compile failed saying "undefined P_WAIT"
// when USE_ITHREADS was commented in the makefile
#ifndef P_WAIT
#define	P_WAIT		0
#endif

#ifndef P_NOWAIT
#define	P_NOWAIT	1
#endif




/*============================================================================================

 Function		:	fnPipeFileMakeArgv

 Description	:	This function makes the argument array.

 Parameters 	:	ptpf	(IN)	-	Input structure.

 Returns		:	Boolean.

==============================================================================================*/

BOOL fnPipeFileMakeArgv(PTEMPPIPEFILE ptpf)
{
        int i=0, j=0;
        int dindex = 0;
        int sindex = 0;

        ptpf->m_argv_len = 0;


        // Below 2 is added for the following reason:
        //   - The first one is for an additional value that will be added through ptpf->m_redirect.
        //   - The second one is for a NULL termination of the array.
        //     This is required for spawnvp API that takes a NULL-terminated array as its 3rd parameter.
        //     If the array is NOT NULL-terminated, then the server abends at the spawnvp call !!
        ptpf->m_argv = (char **) malloc((ptpf->m_pipeCommand->m_argc + 2) * sizeof(char*));
        if (ptpf->m_argv == NULL)
                return FALSE;

        // For memory allocation it is just +1 since the last one is only for NULL-termination
        // and no memory is required to be allocated.
        for(i=0; i<(ptpf->m_pipeCommand->m_argc + 1); i++)
        {
                ptpf->m_argv[i] = (char *) malloc(MAX_DN_BYTES * sizeof(char));
                if (ptpf->m_argv[i] == NULL)
                {
                        for(j=0; j<i; j++)
                        {
                                if(ptpf->m_argv[j])
                                {
                                        free(ptpf->m_argv[j]);
                                        ptpf->m_argv[j] = NULL;
                                }
                        }
                        free(ptpf->m_argv);
                        ptpf->m_argv = NULL;

                        return FALSE;
                }
        }

        // Copy over parsed items, removing "load" keyword if necessary.
        sindex = ((stricmp(ptpf->m_pipeCommand->m_argv[0], LOAD_COMMAND) == 0) ? 1 : 0);
        while (sindex < ptpf->m_pipeCommand->m_argc)
        {
                strcpy(ptpf->m_argv[dindex], ptpf->m_pipeCommand->m_argv[sindex]);
                dindex++;
                sindex++;
        }

        if (stricmp(ptpf->m_argv[0], PERL_COMMAND_NAME) == 0)	// If Perl is the first command.
        {
                ptpf->m_launchPerl = TRUE;

                #ifdef MPK_ON
                        ptpf->m_perlSynchSemaphore = kSemaphoreAlloc((BYTE *)"pipeSemaphore", 0);
                #else
                        ptpf->m_perlSynchSemaphore = OpenLocalSemaphore(0);
                #endif	//MPK_ON
        }
        else if (stricmp(ptpf->m_argv[0], (char *)"perlglob") == 0)
                ptpf->m_doPerlGlob = TRUE;


        // Create last argument, which will redirect to or from the temp file
        if (!ptpf->m_doPerlGlob || ptpf->m_mode)
        {
                if (!ptpf->m_mode)	// If read mode?
                {
                        if (ptpf->m_launchPerl)
                                strcpy(ptpf->m_redirect, (char *)">");
                        else
                                strcpy(ptpf->m_redirect, (char *)"(CLIB_OPT)/>");
                }
                else
                {
                        if (ptpf->m_launchPerl)
                                strcpy(ptpf->m_redirect, (char *)"<");
                        else
                                strcpy(ptpf->m_redirect, (char *)"(CLIB_OPT)/<");
                }
                strcat(ptpf->m_redirect, ptpf->m_fileName);

                if (ptpf->m_launchPerl)
                {
                        char tbuf[15] = {'\0'};
                        sprintf(tbuf, (char *)" -{%x", ptpf->m_perlSynchSemaphore);
                        strcat(ptpf->m_redirect, tbuf);
                }

                strcpy(ptpf->m_argv[dindex], (char*) ptpf->m_redirect);
                dindex++;
        }

        if (dindex < (ptpf->m_pipeCommand->m_argc + 1))
        {
                if(ptpf->m_argv[dindex])
                {
                        free(ptpf->m_argv[dindex]);
                        ptpf->m_argv[dindex] = NULL;	// NULL termination - required for  spawnvp  call.
                }
        }

        ptpf->m_argv_len = dindex;		// Length of the argv array  OR  number of argv string values.
        ptpf->m_argv[ptpf->m_argv_len] = NULL;	// NULL termination - required for  spawnvp  call.


        return TRUE;
}


/*============================================================================================

 Function		:	fnPipeFileOpen

 Description	:	This function opens the pipe file.

 Parameters 	:	ptpf	(IN)	-	Input structure.
                                        command	(IN)	-	Input command string.
                                        mode	(IN)	-	Mode of opening.

 Returns		:	File pointer.

==============================================================================================*/

FILE* fnPipeFileOpen(PTEMPPIPEFILE ptpf, char* command, char* mode)
{
        int i=0, j=0;

        char tempName[_MAX_PATH] = {'\0'};


        ptpf->m_fileName = (char *) malloc(_MAX_PATH * sizeof(char));
        if(ptpf->m_fileName == NULL)
                return NULL;

        // The char array is emptied so that there is no junk characters.
        strncpy(ptpf->m_fileName, "", (_MAX_PATH * sizeof(char)));
        

        // Save off stuff
        //
        if(strchr(mode,'r') != 0)
                ptpf->m_mode = FALSE;	// Read mode
        else if(strchr(mode,'w') != 0)
                ptpf->m_mode = TRUE;	// Write mode
        else
        {
                if(ptpf->m_fileName != NULL)
                {
//			if (strlen(ptpf->m_fileName))
                        if (ptpf->m_fileName)
                                unlink(ptpf->m_fileName);

                        free(ptpf->m_fileName);
                        ptpf->m_fileName = NULL;
                }

                return NULL;
        }


        ptpf->m_pipeCommand = (PCOMMANDLINEPARSER) malloc(sizeof(COMMANDLINEPARSER));
        if (!ptpf->m_pipeCommand)
        {
//		if (strlen(ptpf->m_fileName))
                if (ptpf->m_fileName)
                        unlink(ptpf->m_fileName);

                free(ptpf->m_fileName);
                ptpf->m_fileName = NULL;

                return NULL;
        }

        // Initialise the variables
        ptpf->m_pipeCommand->m_isValid = TRUE;

/****
// Commented since these are not being used.  Still retained here.
// To be removed once things are proved to be working fine to a good confident level,

        ptpf->m_pipeCommand->m_redirInName = NULL;
        ptpf->m_pipeCommand->m_redirOutName = NULL;
        ptpf->m_pipeCommand->m_redirErrName = NULL;
        ptpf->m_pipeCommand->m_redirBothName = NULL;
        ptpf->m_pipeCommand->nextarg = NULL;
****/

        ptpf->m_pipeCommand->sSkippedToken = NULL;
        ptpf->m_pipeCommand->m_argv = NULL;
        ptpf->m_pipeCommand->new_argv = NULL;

        #ifdef MPK_ON
                ptpf->m_pipeCommand->m_qSemaphore = NULL;
        #else
                ptpf->m_pipeCommand->m_qSemaphore = 0L;
        #endif	//MPK_ON

        ptpf->m_pipeCommand->m_noScreen = 0;
        ptpf->m_pipeCommand->m_AutoDestroy = 0;
        ptpf->m_pipeCommand->m_argc = 0;
        ptpf->m_pipeCommand->m_argv_len = 1;


        ptpf->m_pipeCommand->m_argv = (char **) malloc(ptpf->m_pipeCommand->m_argv_len * sizeof(char *));
        if (ptpf->m_pipeCommand->m_argv == NULL)
        {
                free(ptpf->m_pipeCommand);
                ptpf->m_pipeCommand = NULL;

//		if (strlen(ptpf->m_fileName))
                if (ptpf->m_fileName)
                        unlink(ptpf->m_fileName);

                free(ptpf->m_fileName);
                ptpf->m_fileName = NULL;

                return NULL;
        }
        ptpf->m_pipeCommand->m_argv[0] = (char *) malloc(MAX_DN_BYTES * sizeof(char));
        if (ptpf->m_pipeCommand->m_argv[0] == NULL)
        {
                for(j=0; j<i; j++)
                {
                        if(ptpf->m_pipeCommand->m_argv[j])
                        {
                                free(ptpf->m_pipeCommand->m_argv[j]);
                                ptpf->m_pipeCommand->m_argv[j]=NULL;
                        }
                }
                free(ptpf->m_pipeCommand->m_argv);
                ptpf->m_pipeCommand->m_argv=NULL;

                free(ptpf->m_pipeCommand);
                ptpf->m_pipeCommand = NULL;

//		if (strlen(ptpf->m_fileName))
                if (ptpf->m_fileName)
                        unlink(ptpf->m_fileName);

                free(ptpf->m_fileName);
                ptpf->m_fileName = NULL;

                return NULL;
        }


        ptpf->m_redirect = (char *) malloc(MAX_DN_BYTES * sizeof(char));
        if (ptpf->m_redirect == NULL)
        {
                for(i=0; i<ptpf->m_pipeCommand->m_argv_len; i++)
                {
                        if(ptpf->m_pipeCommand->m_argv[i] != NULL)
                        {
                                free(ptpf->m_pipeCommand->m_argv[i]);
                                ptpf->m_pipeCommand->m_argv[i] = NULL;
                        }
                }

                free(ptpf->m_pipeCommand->m_argv);
                ptpf->m_pipeCommand->m_argv = NULL;

                free(ptpf->m_pipeCommand);
                ptpf->m_pipeCommand = NULL;


//		if (strlen(ptpf->m_fileName))
                if (ptpf->m_fileName)
                        unlink(ptpf->m_fileName);

                free(ptpf->m_fileName);
                ptpf->m_fileName = NULL;

                return NULL;
        }

        // The char array is emptied.
        // If it is not done so, then it could contain some junk values and the string length in that case
        // will not be zero.  This causes erroneous results in  fnPipeFileMakeArgv()  function
        // where  strlen(ptpf->m_redirect)  is used as a check for incrementing the parameter count and
        // it will wrongly get incremented in such cases.
        strncpy(ptpf->m_redirect, "", (MAX_DN_BYTES * sizeof(char)));

        // Parse the parameters.
        fnCommandLineParser(ptpf->m_pipeCommand, (char *)command, TRUE);
        if (!ptpf->m_pipeCommand->m_isValid)
        {
                fnTempPipeFileReleaseMemory(ptpf);
                return NULL;
        }


        // Create a temporary file name
        //
        strncpy ( tempName, fnNwGetEnvironmentStr((char *)"TEMP", NWDEFPERLTEMP), (_MAX_PATH - 20) );
        tempName[_MAX_PATH-20] = '\0';
        strcat(tempName, (char *)"\\plXXXXXX.tmp");
        if (!fnMy_MkTemp(tempName))
        {
                fnTempPipeFileReleaseMemory(ptpf);
                return NULL;
        }

        // create a temporary place-holder file
        fclose(fopen(tempName, (char *)"w"));
        strcpy(ptpf->m_fileName, tempName);


        // Make the argument array
        if(!fnPipeFileMakeArgv(ptpf))
        {
                fnTempPipeFileReleaseMemory(ptpf);

                // Release additional memory
                if(ptpf->m_argv != NULL)
                {
                        for(i=0; i<ptpf->m_argv_len; i++)
                        {
                                if(ptpf->m_argv[i] != NULL)
                                {
                                        free(ptpf->m_argv[i]);
                                        ptpf->m_argv[i] = NULL;
                                }
                        }

                        free(ptpf->m_argv);
                        ptpf->m_argv = NULL;
                }

                return NULL;
        }


        // Open the temp file in the appropriate way...
        //
        if (!ptpf->m_mode)	// If Read mode?
        {
                // we wish to spawn a command, intercept its output,
                // and then get that output
                //
                if (!ptpf->m_argv[0])
                {
                        fnTempPipeFileReleaseMemory(ptpf);

                        // Release additional memory
                        if(ptpf->m_argv != NULL)
                        {
                                for(i=0; i<ptpf->m_argv_len; i++)
                                {
                                        if(ptpf->m_argv[i] != NULL)
                                        {
                                                free(ptpf->m_argv[i]);
                                                ptpf->m_argv[i] = NULL;
                                        }
                                }

                                free(ptpf->m_argv);
                                ptpf->m_argv = NULL;
                        }

                        return NULL;
                }

                if (ptpf->m_launchPerl)
                        fnPipeFileDoPerlLaunch(ptpf);
                else
                        if (ptpf->m_doPerlGlob)
                                fnDoPerlGlob(ptpf->m_argv, ptpf->m_fileName);	// hack to do perl globbing
                else
                        spawnvp(P_WAIT, ptpf->m_argv[0], ptpf->m_argv);

                ptpf->m_file = fopen (ptpf->m_fileName, (char *)"r");	// Get the Pipe file handle
        }
        else if (ptpf->m_mode)	// If Write mode?
        {
                // we wish to open the file for writing now and
                // do the command later
                //
                ptpf->m_file = fopen(ptpf->m_fileName, (char *)"w");
        }

        fnTempPipeFileReleaseMemory(ptpf);

        // Release additional memory
        if(ptpf->m_argv != NULL)
        {
                for(i=0; i<(ptpf->m_argv_len); i++)
                {
                        if(ptpf->m_argv[i] != NULL)
                        {
                                free(ptpf->m_argv[i]);
                                ptpf->m_argv[i] = NULL;
                        }
                }

                free(ptpf->m_argv);
                ptpf->m_argv = NULL;
        }

                
        return ptpf->m_file;	// Return the Pipe file handle.
}


/*============================================================================================

 Function		:	fnPipeFileClose

 Description	:	This function closes the pipe file.

 Parameters 	:	ptpf	(IN)	-	Input structure.

 Returns		:	Nothing.

==============================================================================================*/

void fnPipeFileClose(PTEMPPIPEFILE ptpf)
{
        int i = 0;

        if (ptpf->m_mode)	// If Write mode?
        {
                // we wish to spawn a command using our temp file for
                // its input
                //
                if(ptpf->m_file != NULL)
                {
                        fclose (ptpf->m_file);
                        ptpf->m_file = NULL;
                }

                if (ptpf->m_launchPerl)
                        fnPipeFileDoPerlLaunch(ptpf);
                else if (ptpf->m_argv)
                        spawnvp(P_WAIT, ptpf->m_argv[0], ptpf->m_argv);
        }


        // Close the temporary Pipe File, if opened
        if (ptpf->m_file)
        {
                fclose(ptpf->m_file);
                ptpf->m_file = NULL;
        }
        // Delete the temporary Pipe Filename if still valid and free the memory associated with the file name.
        if(ptpf->m_fileName != NULL)
        {
//		if (strlen(ptpf->m_fileName))
                if (ptpf->m_fileName)
                        unlink(ptpf->m_fileName);

                free(ptpf->m_fileName);
                ptpf->m_fileName = NULL;
        }

/**
        if(ptpf->m_argv != NULL)
        {
                for(i=0; i<(ptpf->m_argv_len); i++)
                {
                        if(ptpf->m_argv[i] != NULL)
                        {
                                free(ptpf->m_argv[i]);
                                ptpf->m_argv[i] = NULL;
                        }
                }

                free(ptpf->m_argv);
                ptpf->m_argv = NULL;
        }
**/

        if (ptpf->m_perlSynchSemaphore)
        {
                #ifdef MPK_ON
                        kSemaphoreFree(ptpf->m_perlSynchSemaphore);
                #else
                        CloseLocalSemaphore(ptpf->m_perlSynchSemaphore);
                #endif	//MPK_ON
        }


        return;
}


/*============================================================================================

 Function		:	fnPipeFileDoPerlLaunch

 Description	:	This function launches Perl.

 Parameters 	:	ptpf	(IN)	-	Input structure.

 Returns		:	Nothing.

==============================================================================================*/

void fnPipeFileDoPerlLaunch(PTEMPPIPEFILE ptpf)
{
        char curdir[_MAX_PATH] = {'\0'};
        char* pcwd = NULL;

        int i=0;


        // save off the current working directory to restore later
        // this is just a hack! these problems of synchronization and
        // restoring calling context need a much better solution!
        pcwd = (char *)getcwd(curdir, sizeof(curdir)-1);
        fnSystemCommand(ptpf->m_argv, ptpf->m_argv_len);
        if (ptpf->m_perlSynchSemaphore)
        {
                #ifdef MPK_ON
                        kSemaphoreWait(ptpf->m_perlSynchSemaphore);
                #else
                        WaitOnLocalSemaphore(ptpf->m_perlSynchSemaphore);
                #endif	//MPK_ON
        }

        if (pcwd)
                chdir(pcwd);

        return;
}


/*============================================================================================

 Function		:	fnTempPipeFile

 Description	:	This function initialises the variables of the structure passed in.

 Parameters 	:	ptpf	(IN)	-	Input structure.

 Returns		:	Nothing.

==============================================================================================*/

void fnTempPipeFile(PTEMPPIPEFILE ptpf)
{
        ptpf->m_fileName = NULL;

        ptpf->m_mode = FALSE;	// Default mode = Read mode.
        ptpf->m_file = NULL;
        ptpf->m_pipeCommand = NULL;
        ptpf->m_argv = NULL;

        ptpf->m_redirect = NULL;

        ptpf->m_launchPerl = FALSE;
        ptpf->m_doPerlGlob = FALSE;

        #ifdef MPK_ON
                ptpf->m_perlSynchSemaphore = NULL;
        #else
                ptpf->m_perlSynchSemaphore = 0L;
        #endif

        ptpf->m_argv_len = 0;

        return;
}


/*============================================================================================

 Function		:	fnTempPipeFileReleaseMemory

 Description	:	This function frees the memory allocated to various buffers.

 Parameters 	:	ptpf	(IN)	-	Input structure.

 Returns		:	Nothing.

==============================================================================================*/

void fnTempPipeFileReleaseMemory(PTEMPPIPEFILE ptpf)
{
        int i=0;


        if (ptpf->m_pipeCommand)
        {
                if(ptpf->m_pipeCommand->m_argv != NULL)
                {
                        for(i=0; i<ptpf->m_pipeCommand->m_argv_len; i++)
                        {
                                if(ptpf->m_pipeCommand->m_argv[i] != NULL)
                                {
                                        free(ptpf->m_pipeCommand->m_argv[i]);
                                        ptpf->m_pipeCommand->m_argv[i] = NULL;
                                }
                        }

                        free(ptpf->m_pipeCommand->m_argv);
                        ptpf->m_pipeCommand->m_argv = NULL;
                }

                if(ptpf->m_pipeCommand->sSkippedToken != NULL)
                {
                        free(ptpf->m_pipeCommand->sSkippedToken);
                        ptpf->m_pipeCommand->sSkippedToken = NULL;
                }
/****
// Commented since these are not being used.  Still retained here.
// To be removed once things are proved to be working fine to a good confident level,

                if(ptpf->m_pipeCommand->nextarg)
                {
                        free(ptpf->m_pipeCommand->nextarg);
                        ptpf->m_pipeCommand->nextarg = NULL;
                }

                if(ptpf->m_pipeCommand->m_redirInName)
                {
                        free(ptpf->m_pipeCommand->m_redirInName);
                        ptpf->m_pipeCommand->m_redirInName = NULL;
                }
                if(ptpf->m_pipeCommand->m_redirOutName)
                {
                        free(ptpf->m_pipeCommand->m_redirOutName);
                        ptpf->m_pipeCommand->m_redirOutName = NULL;
                }
                if(ptpf->m_pipeCommand->m_redirErrName)
                {
                        free(ptpf->m_pipeCommand->m_redirErrName);
                        ptpf->m_pipeCommand->m_redirErrName = NULL;
                }
                if(ptpf->m_pipeCommand->m_redirBothName)
                {
                        free(ptpf->m_pipeCommand->m_redirBothName);
                        ptpf->m_pipeCommand->m_redirBothName = NULL;
                }
****/

                if(ptpf->m_pipeCommand != NULL)
                {
                        free(ptpf->m_pipeCommand);
                        ptpf->m_pipeCommand = NULL;
                }
        }

        if(ptpf->m_redirect != NULL)
        {
                free(ptpf->m_redirect);
                ptpf->m_redirect = NULL;
        }

        return;
}