#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef SYSV
     int zc_fork(command,runstring)
#else
     int zc_fork_(command,runstring)
#endif
     char  *command,*runstring;
/*
C----------------------------------------------------------------------
C
C.IDENTIFICATION: INTEGER Function ZC_FORK
C.LIBRARY:        VOS (experimental version)
C.AUTHOR:         L.Chiappetti - IFCTR Milano
C.VERSIONS:       0.0 - 14 Jan 93 - Now supports argument parsing
C                 0.1 - 18 Aug 95 - include directives as zc_execvp
C 
C.PURPOSE:        Fortran-C-interface to run a program in background (NOT TO BE CALLED PUBLICLY)
C.METHOD:         does a fork like system() but without an intermediate shell and without
C                 waiting for the program to return
C
C.SYNTAX:         I = ZC_FORK(command,runstring)
C.PARAMETERS:     CHARACTER*(*) command    : command to be executed (in)
C-                CHARACTER*(*) runstring  : argument string
C.RESTRICTIONS:   maximum of 256 arguments allowed
C.NOTES:          based on zc_system.c
C.FILES:          none
C.REFERENCES:     fork and execvp in C library, strtok for parsing
C
C----------------------------------------------------------------------
*/
{
    int pid;
    int i,j,execvp(),size;
    int argc , retflag ;
    char *argument , *buffer , *locstring , *token, *separator ; 
    char **argarray, **locarg ;
/*
C---- the following parsing code is taken from zc_execvp (D.Dal Fiume) ---------
*/
    retflag = 0 ;
    separator = " " ;
/*
C   allocate space for a working copy of runstring
*/
    i=strlen(runstring);
    locstring = malloc ( (size_t) strlen ( runstring ) ) ;
    strcpy ( locstring , runstring ) ;

    i = 0 ;
    size = 0 ;
/*
C   Parse the runstring a first time to find the number of arguments           
*/
    for ( token = strtok ( locstring , separator ) ; 
          token != NULL ; 
          token = strtok ( NULL , separator ) )
          {
          i += 1 ;
          if ( i > 256 ) {
/*           fprintf ( stderr , " Error: exceeded max no. of args (256)\n" ) ; */
             retflag = 1 ;
             break ; }
         size += strlen(token) + 1 ;
         }
/*
C   this error situation shall never occur (i will always be at least one, since
C   the first argument (arg0 in C-talk) is the command itself)
*/
    if ( i == 0 ) {
       printf ( " Commissioning error in zc_system/parse_string\n" ) ;
       printf ( " No arguments to z_schedule\n") ;
       return (-2) ; }

/*
C   NOW THE REAL JOB STARTS
C   Allocate the memory needed for the array of pointers required by 'execvp'    
C   inclusive of a null pointer at the end 
*/
    i += 1 ;
    argarray = (char**) malloc ( (size_t) (i*sizeof( char* )) ) ;
    locarg = argarray ;
/*
C   Repeat the parsing :
C   Allocate memory in 'argument' 
C   Copy each argument in 'argument' and write the address of the string
C   in the array of pointers 'argarray' 
C   'locarg' is used as a working copy (running pointer) of the pointer 'argarray'    
*/
    strcpy ( locstring , runstring ) ;
    i = 0 ;
    for ( token = strtok ( locstring , separator ) ; 
	  token != NULL ; 
          token = strtok ( NULL , separator ) )
          {
          i += 1 ;
          if ( i > 256 ) break ;
          size = strlen ( token ) + 1 ;
/*        Alignment of memory allocation to shortwords  ?????  */
/*        size = size%2 == 0 ? size : size + 1 ;               */
          argument = malloc ( (size_t) size ) ;
          strcpy ( argument , token ) ;
          *locarg = argument ;
          ++locarg ;
          }
/*
C   Do not forget the terminating NULL pointer! 
*/
    *locarg = (char*) NULL ;
    free ( locstring ) ;
/*
C    if an error occurred earlier return NOW
*/
    if (retflag !=0) return(retflag);

/*---------------------- end of parsing code ---------------------------------*/
/*
C  this is based on the Sun-style schedule, but  wait is removed
C  it is unclear to me what happens if there is an error in fork or execvp
C  probably a -1 status is returned anyhow (see man fork and man wait) 
*/
    pid = fork ();
/*
C   the next two statements are executed in the caller process
C    -1 if the fork is unsuccesful
C    >0 (the pid of the child) if succesful
*/
    if (pid == -1) return(pid);
    if (pid > 0) {
       printf(" spawning pid %i\n",pid);
       return(0);}
/*
C   the rest is executed in the child process  ... 
C   since no checks are made earlier on the existence of the command
C   on the fact it is executable, etc. it fails with a plain message
C--> perhaps there should be some check, or a signal going back to the
C--> parent to tell everything was OK ...
C--> can one have a "partial" wait and then revoke it ? 
C
C     another problem ...
C     if the child needs to do input from the terminal
C     this does not work (the child prompts and dies)
*/
       retflag=execvp(command,argarray);
       if(retflag != 0) {
         printf(" unable to execvp %s\n",command);
         exit(1);}
}
