| iMatix home page
| << | < | > | >>
SFL Logo SFL
Version 1.91

 

process_create

#include "sflproc.h"
PROCESS
process_create (
    const char *filename,               /*  Name of file to execute          */
    char *argv [],                      /*  Arguments for process, or NULL   */
    const char *workdir,                /*  Working directory, or NULL       */
    const char *std_in,                 /*  Stdin device, or NULL            */
    const char *std_out,                /*  Stdout device, or NULL           */
    const char *std_err,                /*  Stderr device, or NULL           */
    char *envv [],                      /*  Environment variables, or NULL   */
    Bool  wait                          /*  Wait for process to end          */
)

Synopsis

Creates a subprocess and returns a PROCESS identifying the new process. Optionally directs standard input, output, and error streams to specified devices. The caller can also specify environment symbols that the subprocess can access. Accepts these arguments:
filename File to execute; if not fully specified, searches PATH.
argv [] List of arguments; argv [0] is filename; ends in a NULL.
workdir Working directory; if NULL, remains in current directory.
std in Device to use for standard input; NULL = no redirection.
std out Device to use for standard output; NULL = no redirection.
std err Device to use for standard error; NULL = no redirection.
envs [] List of environment symbols to define, or NULL.
If argv is NULL, parses the filename argument into words delimited by whitespace and builds the necessary argv table automatically. Use this feature to execute a command with arguments, specified as one string. The envv list consists of strings in the form "name=value", ending in a NULL pointer. If the envv argument is null, the environment of the current process is passed. Otherwise the envv environment is used. If the process is started correctly, this function will sleep for 'process_delay' milliseconds. If the child command detects an error at startup, it may exit with an error status. The sleep allows this error to be collected by calling process status() after this call. If process_delay is 0, any child error is ignored. Returns child process id, or 0 if there was an error. Under VMS, the filename must have been defined as a command before the calling process was started; the path is disregarded. Under OS/2 the filename can be the name of a CMD script, and this will be run with the interpreter specified in the first line (EXTPROC line, or "'/'*!" line; or failing that with the default command interpreter. Known bugs: when parsing filename argument into words, does not handle quotes in any special way; "this text" is 2 words, '"this' and 'text"'. You should have passed the filename through process esc() before adding any optional arguments.

Source Code - (sflproc.c)

{
#if (defined (__UNIX__))
    /*************************************************************************
     **  UNIX  ***************************************************************
     *************************************************************************/

    pid_t
        fork_result;                    /*  Result from fork()               */
    int
        pipe_handle [2],                /*  Parent-to-child pipe             */
        pipe_readsize,                  /*  Amount of data read from pipe    */
        pipe_data;                      /*  Data read from pipe              */
    struct itimerval
        timeout;                        /*  Wait for response from child     */
    struct sigaction
        old_handler;                    /*  Old handler for SIGALRM          */
    const char
        *filename_only;                 /*  Filename, without arguments      */
    char
        *clean_filename,                /*  Unescaped filename               */
        *full_filename;                 /*  File to execute, with path       */

    /*  Create pipe for feedback from child to parent; quit if this fails    */
    if (pipe (pipe_handle))
        return (0);

    /*  Create subprocess - this returns 0 if we are the child, the pid if   */
    /*  we are the parent, or -1 if there was an error (not enough memory).  */
    fork_result = fork ();

    if (fork_result < 0)                /*  < 0 is an error                  */
      {
        close (pipe_handle [0]);        /*  Close the pipe                   */
        close (pipe_handle [1]);
        return (0);                     /*  Could not fork                   */
      }
    else
    if (fork_result > 0)                /*  > 0 is the parent process        */
      {
        /*  --- PARENT PROCESS HANDLING ------------------------------------ */
        /*  If the child process has a problem with the exec() call, it      */
        /*  sends us an errno value across the pipe.  If the exec() call     */
        /*  works okay, we get no feedback across the pipe.  We wait for a   */
        /*  small time (number of msecs specified by process_delay).  If     */
        /*  nothing comes across the pipe, we assume everything went okay.   */
        /*  The FD_CLOEXEC setting *should* cause the child pipe to close    */
        /*  after exec() but this does not seem to work; the read() still    */
        /*  blocks.  Bummer.                                                 */

        if (process_delay > 0)
          {
            timeout.it_interval.tv_sec  = 0;
            timeout.it_interval.tv_usec = 0;
            timeout.it_value.tv_sec     =  process_delay / 1000;
            timeout.it_value.tv_usec    = (process_delay % 1000) * 1000;

            /*  Save old signal handler to be polite to the calling program  */
            /*  then redirect the SIGALRM signal to our own (empty) handler  */
            sigaction (SIGALRM, NULL, &old_handler);
            signal    (SIGALRM, ignore_signal);
            setitimer (ITIMER_REAL, &timeout, 0);

            /*  Now read on the pipe until data arrives or the alarm goes    */
            pipe_readsize = read (pipe_handle [0], &pipe_data, sizeof (errno));

            /*  Restore old signal handler                                   */
            sigaction (SIGALRM, &old_handler, NULL);

          }
        else
            pipe_readsize = 0;

        close (pipe_handle [0]);        /*  Close the pipe                   */
        close (pipe_handle [1]);
        if (pipe_readsize == -1)
          {
            if (errno == EBADF || errno == EINTR)
              {
                /*  Normal - SIGALRM arrived or FD_CLOEXEC worked :)         */
                if (wait)
                    waitpid (fork_result, 0, 0);
                return ((PROCESS) fork_result);
              }
            else
                return (0);             /*  Error on read()                  */
          }
        else
        /*  We come here if process_delay was zero, or FD_CLOEXEC did its    */
        /*  job and the pipe was closed by the child process.                */
        if (pipe_readsize == 0)
          {
            if (wait)
                waitpid (fork_result, 0, 0);
            return ((PROCESS) fork_result);
          }
        else
          {
            /*  We read data from the pipe - this is an error feedback from  */
            /*  the child - i.e. file not found, or a permission problem.    */
            errno = pipe_data;          /*  Stuff the errno                  */
            return (0);
          }
      }
    /*  --- CHILD PROCESS HANDLING ----------------------------------------- */
    /*  Prepare the process environment and execute the file                 */

    /*  If argv[] array was not supplied, build it now from filename         */
    /*  And pull out the name of the file that we want to run.               */
    if (!argv)
      { /*  Split off the arguments, and pick out the filename to use        */
        argv = tok split (filename);

        /*  The filename, and only the filename, is the 0th argument         */
        filename_only = argv[0];
      }
    else
      { /*  Already got our arguments, so the filename is just the filename  */
        filename_only = filename;
      }

    /*  If requested, close stdin, stdout, stderr, and redirect them         */
    redirect_io (std_in,  STDIN_FILENO,  pipe_handle [1], TRUE);
    redirect_io (std_out, STDOUT_FILENO, pipe_handle [1], FALSE);
    redirect_io (std_err, STDERR_FILENO, pipe_handle [1], FALSE);

    /*  Find file on path, make sure it is executable                        */
    /*  This is a good moment to unescape any spaces in the filename...      */

    clean_filename = process unesc (NULL, filename_only);
    if (strchr (clean_filename, '/')     == NULL
    &&  strchr (clean_filename, PATHEND) == NULL)
        full_filename = file where ('r', "PATH", clean_filename, NULL);
    else
        full_filename = file where ('r',  NULL,  clean_filename, NULL);
    mem_free (clean_filename);

    if (full_filename == NULL)
      {
        errno = ENOENT;                 /*  No such file                     */
        write (pipe_handle [1], &errno, sizeof (errno));
        exit (EXIT_FAILURE);            /*  Kill the child process           */
      }
    if (!file is executable (full_filename))
      {
        errno = EACCES;                 /*  No permission to access file     */
        write (pipe_handle [1], &errno, sizeof (errno));
        exit (EXIT_FAILURE);            /*  Kill the child process           */
      }

    /*  Tell the system to close the pipe when we've done the exec()         */
    fcntl (pipe_handle [0], F_SETFD, FD_CLOEXEC);
    fcntl (pipe_handle [1], F_SETFD, FD_CLOEXEC);

    /*  If requested, change to working directory                            */
    if (workdir)
        chdir (workdir);

    /*  Execute the program - normally this call does not return, as it      */
    /*  replaces the current process image by the new one.  If we ever do    */
    /*  return, it is because there was an error.                            */
    if (envv)                           /*  If caller provided envv, use it  */
        execve (full_filename, argv, envv);
    else                                /*  Otherwise use current values     */
        execv  (full_filename, argv);

    write (pipe_handle [1], &errno, sizeof (errno));
    exit (EXIT_FAILURE);                /*  Kill the child process           */

#elif (defined (__OS2__))
    /*************************************************************************
     **  OS/2  ***************************************************************
     *************************************************************************/

    int
        process = 0;                    /*  Process number                   */
    HANDLE
        old_stdin = -1,                 /*  Dup'd handle for old stdin       */
        old_stdout = -1,                /*  Dup'd handle for old stdout      */
        old_stderr = -1;                /*  Dup'd handle for old stderr      */
    int parsedargs = 0,                 /*  argv() points at token array     */
        free_argv  = 0;                 /*  argv() points at handbuilt array */
    const char
        *filename_only = NULL,          /*  Filename, without arguments      */
        *actual_command = NULL;         /*  Actual command string to run     */
    char
        *clean_filename = NULL,         /*  Unescaped filename               */
        *full_filename = NULL,          /*  File to execute, with path       */
        *curdir = NULL,                 /*  Current working directory        */
        *strfree_this = NULL;           /*  strfree() this, if not NULL      */

    /*  NOTE: special care must be taken to ensure this code does not leak   */
    /*  memory, as the memory will be leaked in the main process which       */
    /*  potientally tries to run for a long period of time.  Token arrays    */
    /*  have a lot of potiental for leaks if care is not taken.  To avoid    */
    /*  these potiental problems strings are copied a little more than       */
    /*  otherwise would have been done, and then the original token arrays   */
    /*  are freed.                                                           */

    /*  If argv[] array was not supplied, build it now from filename         */
    /*  And pull out the name of the file that we want to run.               */
    if (!argv)
    {   /*  Split off the arguments, and pick out the filename to use        */
        argv = tok split (filename);

        /*  The filename, and only the filename, is the 0th argument         */
        filename_only = argv[0];

        parsedargs = 1;                 /* Yes, we split off the arguments   */
    }
    else
    {   /*  Already got our arguments, so the filename is just the filename  */
        filename_only = filename;
    }

    /*  Under OS/2, we accept the magic file headers "#!", and "'/'*!".      */
    /*  We also special case running CMD scripts, so that we invoke the      */
    /*  default command interpreter, with a "/c" parameter, and the script   */
    /*  name.  The magic file headers are checked first so can be used to    */
    /*  override the default command interpreter.                            */

    actual_command = redirect_exec (filename_only);

    if (actual_command != NULL)
    {
        /*  At this point we have a string containing the name of the        */
        /*  program to run, followed by the arguments and the scriptname,    */
        /*  if it was a script that we were going to run.  So we tokenise the*/
        /*  string we got back and arrange for those bits to end up in the   */
        /*  arguments if required.                                           */
        char **newargs = NULL;
        int num_new = 0, num_existing = 0;
        int  free_newargs = 0;

        newargs = tok split (actual_command);  /*  Split off the arguments   */

        actual_command = newargs[0];

        /*  Count the number of new arguments (should be at least 1)         */
        /*  And while we are here, eliminate any double quotes around the    */
        /*  arguments (especially the script name), since they'll only get   */
        /*  in the way later.                                                */
        for (num_new = 0; newargs[num_new] != NULL; num_new++)
            if (*newargs[num_new] == '"')
            {  char *pair = NULL;
               pair = strrchr(newargs[num_new], '"');
               if (pair != NULL)
               {  *pair = '\0';                /* Eliminate the last "       */
                  newargs[num_new]++;          /* Step over the first one    */
               }
            }

        ASSERT(num_new >= 1);

        /*  Count the number of existing arguments (from above), should be   */
        /*  at least 1.                                                      */
        for (num_existing = 0; argv[num_existing] != NULL; num_existing++)
            ;  /* EMPTY BODY */

        ASSERT(num_existing >= 1);

        /*  Handle .CMD script files where the redirection wasn't done above */
        if (num_new == 1)
        {   /*  Okay, it didn't expand there.  But possibly we have a CMD    */
            /*  script and need to invoke the command processor.             */
            char *extension = NULL;

            /*  Find file extension; if not found, set to NULL               */
            extension = strrchr (actual_command, '.');
            if (extension == NULL
            ||  strchr (extension, '/')         /*  Last '.' is part of path */
            ||  strchr (extension, '\\'))       /*  => filename has no ext   */
                extension = NULL;

            if (extension != NULL && (lexcmp(extension, ".CMD") == 0))
            {   /* This is a CMD script, and we need to invoke the command   */
                /* interpreter over it.                                      */
                char *command_processor = NULL;

                command_processor = strdupl (env get string ("COMSPEC", ""));

                if (*command_processor != '\0') /*  Not an empty string      */
                {   /*  Determine command processor arguments                */
                    char **cmdargs = NULL;
                    char **tmpargs = NULL;

                    cmdargs = tok split (command_processor);

                    /*  Count the number of new arguments (at least 1)       */
                    for (num_new = 0; cmdargs[num_new] != NULL; num_new++)
                        ;    /* EMPTY BODY */

                    ASSERT(num_new >= 1);

                    /*  Now merge those arguments with script name           */
                    /*  Need: num_new + 1 for "/c", +1 for script name       */
                    /*        + 1 to terminate array                         */
                    tmpargs = mem_alloc((num_new+3) * sizeof(char *));

                    if (tmpargs != NULL)
                    {   /*  Okay, copy all the arguments into place          */
                        int i = 0;

                        for (i = 0; i < num_new; i++)
                           tmpargs[i] = strdupl (cmdargs[i]);

                        tmpargs[num_new++] = strdupl ("/c");
                        tmpargs[num_new++] = strdupl (actual_command);
                        tmpargs[num_new]   = NULL;

                        /*  Free the old arguments, and the old parse        */
                        tok free(newargs);
                        tok free(cmdargs);

                        /*  Now use that for our new arguments               */
                        newargs = tmpargs;
                        actual_command = newargs[0];
                        free_newargs = 1;       /*  Must free newargs later  */
                    }

                    /*  Free the command processor string                    */
                    strfree(&command_processor);
                }
            }   /* extension is .cmd                                         */
        }   /*  only one new argument (filename to run)                      */

        /*  Now collect all the arguments together into one array            */

        if (num_new >= 2 && num_existing >= 2)
        {   /*  Okay, we've got arguments to merge together, so we put the   */
            /*  new ones first followed by the old ones.                     */
            char **tmpargs;

            ASSERT(newargs != NULL);

            /*  Allocate space for the new arguments (at start), and the     */
            /*  existing arumgnents (at end), and a terminator.              */
            tmpargs = mem_alloc((num_new+num_existing+1) * sizeof(char *));
            if (tmpargs != NULL)
            {   /*  Okay, copy all the arguments into place                  */
                int i = 0;

                for (i = 0; i < num_new; i++)
                    tmpargs[i] = strdupl (newargs[i]);

                /*  NOTE: We skip the first argument here, since it is the  */
                /*  name of the script, and we've got one of those above.   */
                /*  BUT we've got to put next arg in next position, hence -1*/

                for (i = 1; i < num_existing; i++)
                    tmpargs[num_new + i - 1] = strdupl (argv[i]);

                /*  Terminate the array of arguments                        */
                tmpargs[num_new + num_existing - 1] = NULL;

                /*  Pick up a new pointer to the command to run             */
                actual_command = tmpargs[0];

                /*  Tidy up after ourselves                                  */
                if (free_newargs)
                  {
                    int j = 0;
                    for (j = 0; newargs[j] != NULL; j++)
                        strfree(&newargs[j]);

                    mem_free(newargs);
                  }
                else
                    tok free (newargs);

                if (parsedargs)
                  {
                    tok free(argv);
                    parsedargs = 0;
                  }

                /*  Change pointer to point at the new (combined) arguments  */
                argv = tmpargs;
                free_argv = 1;
            }
            else
            {   /*  We couldn't allocate the new memory required             */
                /*  Return failure.                                          */
                tok free(newargs);
                if (parsedargs)
                   tok free(argv);
                errno = ENOMEM;
                return ((PROCESS)0);
            }
        }
        else if (num_new >= 2 && num_existing <= 1)
        {   /*  There were no arguments before, there are now.  Use new ones */
            if (parsedargs)
            {  /* We parsed the arguments, free up some of the memory        */
               tok free(argv);
               parsedargs = 0;
            }

            argv = newargs;
            if (free_newargs)               /* Make sure we free arguments   */
                free_argv = 1;
            else
                parsedargs = 1;
        }
        else /* (num_new <= 1) */   /* num_existing is 1 or more             */
        {   /*  No expansion of the string, we just use the existing args    */
            /*  But we do use the string as returned, because it may have    */
            /*  an extension on it.                                          */
            ASSERT(num_new <= 1);
            ASSERT(num_existing >= 1);

            /*  Copy the string as returned, so that we can use it below     */
            strfree_this = strdupl (actual_command);
            if (strfree_this != NULL)
            {
                actual_command = strfree_this;
                ASSERT(free_newargs == 0);
                tok free(newargs);
            }
        }
    }   /*  Redirection found a filename to run */
    else
    {   /*  Redirection failed.  This means that it isn't executable, because*/
        /*  we should either have got a full name back, or a command string  */
        /*  to run.                                                          */

        if (parsedargs)
           tok free(argv);
        errno = EACCES;                 /*  No permission to access file     */
        return (PROCESS)0;
    }

    /*  Find file on path, make sure it is executable                        */
    /*  This is a good moment to unescape any spaces in the filename...      */
    clean_filename = process unesc (NULL, actual_command);
    if (strchr (clean_filename, '/')     == NULL
    &&  strchr (clean_filename, PATHEND) == NULL)
        full_filename = file where ('r', "PATH", clean_filename, NULL);
    else
        full_filename = file where ('r',  NULL,  clean_filename, NULL);
    mem_free (clean_filename);

    if (full_filename == NULL)
      {
        /*  Clear out the memory that we don't need any longer               */
        if (parsedargs)
            tok free(argv);
        else if (free_argv)
          {
            int j = 0;
            for (j = 0; argv[j] != NULL; j++)
                strfree(&argv[j]);
            mem_free(argv);
          }
        if (strfree_this != NULL)
            strfree(&strfree_this);

        errno = ENOENT;                 /*  No such file                     */
        return (PROCESS)0;              /*  Failed to open                   */
      }
    if (!file is executable (full_filename))
      {
        /*  Clear out the memory that we don't need any longer               */
        if (parsedargs)
            tok free(argv);
        else if (free_argv)
          {
            int j = 0;
            for (j = 0; argv[j] != NULL; j++)
                strfree(&argv[j]);
            mem_free(argv);
          }
        if (strfree_this != NULL)
           strfree(&strfree_this);

        errno = EACCES;                 /*  No permission to access file     */
        return (PROCESS)0;
      }

    /*  Redirect the IO streams, and save copies of the ones we redirect     */
    old_stdin  = redirect_io(std_in,  STDIN_FILENO,  0, TRUE);
    old_stdout = redirect_io(std_out, STDOUT_FILENO, 0, FALSE);
    old_stderr = redirect_io(std_err, STDERR_FILENO, 0, FALSE);

    if (old_stdin == -2 || old_stdout == -2 || old_stderr == -2)
    {   /* An error redirecting one of the file handles; restore them all    */
        /* and exit having failed our job.                                   */
        restore_redirection(old_stdin, old_stdout, old_stderr);

        /*  Clear out the memory that we don't need any longer               */
        if (parsedargs)
            tok free(argv);
        else if (free_argv)
          {
            int j = 0;
            for (j = 0; argv[j] != NULL; j++)
                strfree(&argv[j]);
            mem_free(argv);
          }
        if (strfree_this != NULL)
           strfree(&strfree_this);

        return (PROCESS)0;
    }

    /*  If requested, change to working directory                            */
    if (workdir)
    {
        curdir = getcwd(NULL, 256);
        chdir (workdir);
    }
    else
        curdir = NULL;

    /*  Spawn the new program, and pick up its process ID.                   */
    if (envv)                           /*  If caller provided envv, use it  */
        process = spawnve (P_NOWAIT, full_filename, argv, envv);
    else                                /*  Otherwise use the current values */
        process = spawnv  (P_NOWAIT, full_filename, argv);

    /*  Put things back the way they were before                             */
    restore_redirection(old_stdin, old_stdout, old_stderr);
    if (curdir != NULL)                 /*  If directory changed, restore it */
    {
        chdir(curdir);
        free(curdir);
    }

    /*  Clear out the memory that we don't need any longer                   */
    if (parsedargs)
        tok free(argv);
    else if (free_argv)
    {
        int j = 0;
        for (j = 0; argv[j] != NULL; j++)
            strfree(&argv[j]);

        mem_free(argv);
    }
    if (strfree_this != NULL)
        strfree(&strfree_this);

    if (process <= -1)
        return ((PROCESS)0);            /*  Error starting child process     */

    if (wait)
        waitpid (process, 0, 0);

    return ((PROCESS) process);

#elif (defined (WIN32))
    /*************************************************************************
     **  WINDOWS 32  *********************************************************
     *************************************************************************/

    PROCESS
        process;                        /*  Our created process handle       */
    STARTUPINFO
        newinfo = {0},                  /*  Specification for new process    */
        curinfo;                        /*  Specification of cur process     */
    PROCESS_INFORMATION
        procinfo;                       /*  Information about created proc   */
    DWORD
        dwCreateFlags = CREATE_NEW_CONSOLE;
    char
        *olddir,                        /*  Caller's working directory       */
        *fulldir,                       /*  Process' working directory       */
        *args,                          /*  Command arguments, if any        */
        *actual_command,                /*  Command, possibly qualified      */
        *buffer = NULL;                 /*  Working buffer                   */
    int
        argn;                           /*  Argument number                  */

    /*  Format full working directory, if specified                          */
    if (workdir)
      {
        olddir = get curdir ();         /*  Just a lazy way to let the OS    */
        set curdir (workdir);           /*  figure-out if the workdir is a   */
        fulldir = get curdir ();        /*  relative or absolute directory.  */
        set curdir (olddir);
        mem_free (olddir);
      }
    else
        fulldir = NULL;

    /*  Under Windows we accept the magic file header "#!".  If the          */
    /*  caller supplied an argument list, we attach this to the command.     */
    actual_command = redirect_exec (filename);
    strconvch (actual_command, '/', '\\');
    GetShortPathName (actual_command, actual_command,
                              strlen (actual_command) + 1);

    args = strchr (filename, ' ');      /*  Find arguments, if any           */
    if (argv)
      {                                 /*  Build full command buffer        */
        buffer = mem_alloc (tok text size ((char **) argv)
                                + strlen (actual_command) + 1);
        strcpy (buffer, actual_command);
        for (argn = 1; argv [argn]; argn++)
            xstrcat (buffer, " ", argv [argn], NULL);
        actual_command = buffer;
      }
    else
    if (args)
      {
        buffer = xstrcpy (NULL, actual_command, args, NULL);
        actual_command = buffer;
      }
    process = mem_alloc (sizeof (PROC_HANDLE));
    process-> process = NULL;
    process-> in  = redirect_io (std_in,  0, 0, TRUE);
    process-> out = redirect_io (std_out, 0, 0, FALSE);
    process-> err = redirect_io (std_err, 0, 0, FALSE);

    /*  Convert environment to a Windows-type packed block of strings        */
    /*  Use supplied environment, or parent environment if necessary.        */
    process-> envd = strt2descr (envv? (char **) envv: environ);

    GetStartupInfo (&curinfo);
    newinfo.cb          = sizeof (newinfo);
    newinfo.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    newinfo.wShowWindow = SW_HIDE;
    newinfo.hStdInput   = process-> in?  process-> in:  curinfo.hStdInput;
    newinfo.hStdOutput  = process-> out? process-> out: curinfo.hStdOutput;
    newinfo.hStdError   = process-> err? process-> err: curinfo.hStdError;
    newinfo.lpTitle     = NULL;

    /*  If necessary, run in separate VM, for 16-bit programs                */
    if (process_compatible)
        dwCreateFlags |= CREATE_SEPARATE_WOW_VDM;

    /*  CreateProcess returns errors sometimes, even when the process was    */
    /*  started correctly.  The cause is not evident.  For now: we detect    */
    /*  an error by checking the value of procinfo.hProcess after the call.  */
    procinfo.hProcess = NULL;
    CreateProcess (
        NULL,                           /*  Name of executable module        */
        actual_command,                 /*  Command line string              */
        NULL,                           /*  Process security attributes      */
        NULL,                           /*  Thread security attributes       */
        TRUE,                           /*  Handle inheritance flag          */
        dwCreateFlags,                  /*  Creation flags                   */
        process-> envd-> data,          /*  New environment block            */
        fulldir,                        /*  Current directory name           */
        &newinfo,                       /*  STARTUPINFO                      */
        &procinfo);                     /*  PROCESS_INFORMATION              */

    mem_strfree (&fulldir);
    mem_strfree (&buffer);              /*  Deallocate buffer, if used       */

    if (procinfo.hProcess == NULL)      /*  Error, we presume                */
      {
        process close (process);
        return (NULL);
      }

    /*  Release our hold on the thread                                       */
    CloseHandle (procinfo.hThread);
    process-> process = procinfo.hProcess;

    /*  We do not need access to the files any longer in this process        */
    if (process-> in)
      {
        CloseHandle (process-> in);
        process-> in = NULL;
      }
    if (process-> out)
      {
        CloseHandle (process-> out);
        process-> out = NULL;
      }
    if (process-> err)
      {
        CloseHandle (process-> err);
        process-> err = NULL;
      }

    /*  Wait for the process to finish or be cancelled                       */
    if (wait)
      {
        WaitForSingleObject (procinfo.hProcess, INFINITE);
        process close (process);
      }
    return (process);

#elif (defined (__VMS__))
    /*************************************************************************
     **  OPENVMS  ************************************************************
     *************************************************************************/

    PROCESS
        process;                        /*  Our created process handle       */
    char
        *curdir,                        /*  Current directory                */
        *clean_filename,                /*  Unescaped filename               */
        *full_filename = NULL,
        *full_std_in   = NULL,
        *full_std_out  = NULL;
    qbyte
        process_flags;                  /*  Process creation flags           */
    int
        argn,                           /*  Argument number                  */
        rc;                             /*  Return code from lib$spawn       */
    Bool
        rebuilt_argv = FALSE;           /*  Did we rebuild argv[]?           */

    VMS_STRING (command_dsc, "");       /*  Define string descriptors        */
    VMS_STRING (std_in_dsc,  "");
    VMS_STRING (std_out_dsc, "");

    /*  If argv[] array was not supplied, build it now from filename         */
    if (!argv)
      {
        argv = tok split (filename);
        filename = argv [0];
        rebuilt_argv = TRUE;
      }
    /*  If filename contains a path or extension, disregard them             */
    clean_filename = strrchr (filename, '/');
    if (clean_filename)
        clean_filename++;
    else
        clean_filename = (char *) filename;
    if (strchr (clean_filename, '.'))
       *strchr (clean_filename, '.') = '\0';

    /*  Rebuild full command from filename and arguments                     */
    full_filename = mem_alloc (tok text size ((char **) argv)
                               + strlen (clean_filename) + 1);
    strcpy (full_filename, clean_filename);
    for (argn = 1; argv [argn]; argn++)
        xstrcat (full_filename, " ", argv [argn], NULL);

    /*  Free argument table if we allocated it dynamically here              */
    if (rebuilt_argv)
        tok free (argv);

    command_dsc.value  = full_filename;
    command_dsc.length = strlen (full_filename);

    /*  Prepare full names for stdin and stdout                              */
    curdir = get curdir ();
    if (std_in)
      {
        if (strchr (std_in, '/'))       /*  If already with path, use as is  */
            full_std_in = mem_strdup (std_in);
        else
            full_std_in = xstrcpy (NULL, curdir, "/", std_in, NULL);

        translate_to_vms  (full_std_in);
        std_in_dsc.value = full_std_in;
      }
    if (std_out)
      {
        if (strchr (std_out, '/'))      /*  If already with path, use as is  */
            full_std_out = mem_strdup (std_out);
        else
            full_std_out = xstrcpy (NULL, curdir, "/", std_out, NULL);

        translate_to_vms   (full_std_out);
        std_out_dsc.value = full_std_out;
      }
    std_in_dsc.length  = std_in?  strlen (std_in_dsc.value): 0;
    std_out_dsc.length = std_out? strlen (std_out_dsc.value): 0;

    /*  If requested, change to working directory                            */
    if (workdir)
        chdir (workdir);

    /*  Prepare process flags                                                */
    if (wait)
        process_flags = 0;
    else
        process_flags = 1;              /*  Bit 1 = don't wait for child     */

    process = mem_alloc (sizeof (PROC_HANDLE));
    process-> id     = 0;
    process-> status = 0;               /*  Completion status                */

/*  char *envv [],  */                  /*  Environment variables, or NULL   */

    rc = lib$spawn (
        &command_dsc,                   /*  Command to run                   */
        std_in?  &std_in_dsc: NULL,     /*  Stdin descriptor                 */
        std_out? &std_out_dsc: NULL,    /*  Stdout+stderr                    */
        &process_flags,                 /*  Options for new process          */
        &NULL,                          /*  Process name -- generated        */
        &process-> id,                  /*  Returned process ID              */
        &process-> status);

    if (workdir)                        /*  Switch back to original dir      */
        chdir (curdir);
    mem_free (curdir);

    mem_strfree (&full_filename);       /*  Deallocate various buffers,      */
    mem_strfree (&full_std_in);         /*    if they were used              */
    mem_strfree (&full_std_out);        /*                                   */

    /*  Return process ID.  If we waited for completion, the process id      */
    /*  is always NULL.                                                      */
    if (rc != 1)                        /*  Process failed with error        */
      {
        process close (process);
        process = NULL;
      }
    else
    if (wait)                           /*  Finished with process            */
        process close (process);

    return (process);

#else
    return ((PROCESS) 0);               /*  Not supported on this system     */
#endif
}

| << | < | > | >> iMatix Copyright © 1996-98 iMatix