/* Derived from fork-child.c by Eberhard Mattes -- Jun 1996 */
/* Spawn an emx child process, and set up to debug it, for GDB.
   Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
   Contributed by Cygnus Support.

This file is part of GDB.

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., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "defs.h"
#include "frame.h"  /* required by inferior.h */
#include "inferior.h"
#include "target.h"
#include "gdbcore.h"
#include "terminal.h"		/* For #ifdef TIOCGPGRP and new_tty */

#include <signal.h>

#include <process.h>
#include <fcntl.h>

#define NHANDLES 64

struct desc;

extern int close_sessions;
extern struct desc *desc_list;
extern int switch_sessions;

static char **cmdline_argv;
static int cmdline_argc;
static char *prev_inferior_args;

/* Start an inferior emx child process and sets inferior_pid to its pid.
   EXEC_FILE is the file to run.
   ALLARGS is a string containing the arguments to the program.
   ENV is the environment vector to pass.  Errors reported with error().  */

#define WHITE(c) ((c) == ' ' || (c) == '\t')

void
fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
               shell_file)
     char *exec_file;
     char *allargs;
     char **env;
     void (*traceme_fun) PARAMS ((void));
     void (*init_trace_fun) PARAMS ((int));
     char *shell_file;
{
  int pid, i, handle, open_mode, redir;
  char *handle_name[NHANDLES];
  int handle_saved[NHANDLES];
  int handle_child[NHANDLES];
  int handle_mode[NHANDLES];
  char *string0, *q, **argv;

  /* If no exec file handed to us, get it from the exec-file command -- with
     a good, common error message if none is specified.  */
  if (exec_file == 0)
    exec_file = get_exec_file(1);

  if (_osmode == OS2_MODE && emx_is_pm (exec_file))
    {
      if (emx_this_vio ())
        {
          printf_unfiltered ("You are running GDB in a VIO window and try to debug a PM application.\n");
          printf_unfiltered ("Doing so will hang your system.\n");
          if (!query ("Do you really want to continue? "))
            error ("Good idea.  Start GDB in a full-screen session and try again.");
        }
      else if (!switch_sessions)
        {
          printf_unfiltered ("Debugging a PM program with `set switch off' will hang your system.\n");
          if (!query ("Do you really want to continue? "))
            error ("Good idea.  Use `set switch on', then try again.");
        }
    }
  close_exec_file ();
  for (i = 0; i < NHANDLES; ++i)
    handle_name[i] = NULL;
  handle_name[0] = "con"; handle_mode[0] = O_RDONLY;
  handle_name[1] = "con"; handle_mode[1] = O_WRONLY;
  handle_name[2] = "con"; handle_mode[2] = O_WRONLY;

  if (allargs != prev_inferior_args)
    cmdline_argv = NULL;

  if (cmdline_argv != NULL)
    {
      string0 = NULL;
      argv = xmalloc ((cmdline_argc + 2) * sizeof (argv[0]));
      argv[0] = exec_file;
      for (i = 0; i < cmdline_argc; ++i)
        argv[i+1] = cmdline_argv[i];
      argv[cmdline_argc+1] = NULL;
    }
  else
    {
      int arga, argc, src, dst, bs, quote;
      char *string;

      string = string0 = concat (exec_file, " ", allargs, NULL);
      argv = NULL; argc = 0; arga = 0; dst = 0; src = 0;
      while (WHITE (string[src]))
        ++src;
      do
        {
          if (argc >= arga)
            {
              arga += 10;
              argv = (char **)xrealloc (argv, arga * sizeof (argv[0]));
            }
          redir = 0;
          if (string[src] == 0)
            q = NULL;
          else
            {
              q = string + dst; bs = 0; quote = 0;
              for (;;)
                {
                  if (string[src] == '"')
                    {
                      while (bs >= 2)
                        {
                          string[dst++] = '\\';
                          bs -= 2;
                        }
                      if (bs & 1)
                        string[dst++] = '"';
                      else
                        quote = !quote;
                      bs = 0;
                    }
                  else if (string[src] == '\\')
                    ++bs;
                  else
                    {
                      while (bs != 0)
                        {
                          string[dst++] = '\\';
                          --bs;
                        }
                      if (string[src] == 0 || (WHITE (string[src]) && !quote))
                        break;
                      if ((string[src] == '<' || string[src] == '>') && !quote)
                        {
                          if (redir)
                            break;
                          redir = 1;
                          if (string + dst == q)
                            handle = -1; /* Default handle */
                          else if (string + dst == q + 1
                                   && *q >= '0' && *q <= '9')
                            handle = *q - '0';
                          else
                            {
                              handle = -1; /* Default handle */
                              /* Contribute to the argument list. */
                              string[dst++] = 0;
                              argv[argc++] = q;
                            }
                          if (string[src] == '<')
                            {
                              if (handle == -1)
                                handle = 0;
                              open_mode = O_RDONLY | O_TEXT;
                            }
                          else
                            {
                              if (handle == -1)
                                handle = 1;
                              if (string[src+1] == '>')
                                {
                                  ++src;
                                  open_mode = O_RDWR | O_TEXT | O_CREAT | O_APPEND;
                                }
                              else
                                open_mode = O_RDWR | O_TEXT | O_CREAT | O_TRUNC;
                            }
                          if (handle < NHANDLES)
                            {
                              handle_name[handle] = string + dst;
                              handle_mode[handle] = open_mode;
                            }
                        }
                      else
                        string[dst++] = string[src];
                    }
                  ++src;
                }
              while (WHITE (string[src]))
                ++src;
              string[dst++] = 0;
            }
          if (!redir)
            argv[argc++] = q;
        } while (q != NULL);
    }

  for (i = 0; i < NHANDLES; ++i)
    if (handle_name[i] != NULL)
      handle_saved[i] = dup (i);
  for (i = 0; i < NHANDLES; ++i)
    if (handle_name[i] != NULL)
      {
        handle = open (handle_name[i], handle_mode[i], 0600);
        if (handle < 0)
          perror_with_name (handle_name[i]);
        handle_child[i] = handle;
        if (handle_mode[i] & O_APPEND)
          lseek (handle, 0L, SEEK_END);
      }
  for (i = 0; i < NHANDLES; ++i)
    if (handle_name[i] != NULL)
      {
        dup2 (handle_child[i], i);
        close (handle_child[i]);
      }
  for (i = 0; i < NHANDLES; ++i)
    if (handle_name[i] == NULL)
      fcntl (i, F_SETFD, 1);
  
  pid = spawnve (P_DEBUG | (close_sessions ? 0 : P_NOCLOSE)
                 | (desc_list != NULL ? P_DEBUGDESC : 0),
                 exec_file, argv, env);

  for (i = 0; i < NHANDLES; ++i)
    if (handle_name[i] != NULL)
      {
        dup2 (handle_saved[i], i);
        close (handle_saved[i]);
      }

  if (string0 != NULL)
    free (string0);
  free (argv);

  if (pid < 0)
    perror_with_name ("spawnve");

  /* Now that we have a child process, make it our target, and
     initialize anything target-vector-specific that needs initializing.  */
  (*init_trace_fun)(pid);

#ifdef CREATE_INFERIOR_HOOK
  CREATE_INFERIOR_HOOK (pid);
#endif  
  inferior_pid = pid;
  clear_proceed_status ();
  init_wait_for_inferior ();
  target_terminal_init ();
  target_terminal_inferior ();
  stop_soon_quietly = 0;
}


void
startup_inferior (ntraps)
     int ntraps;
{
}


void
emx_inferior_args (char **p)
{
  int i;
  size_t size;
  char *string;

  if (cmdline_argv != NULL)
    {
      size = 0;
      for (i = 0; i < cmdline_argc; ++i)
        size += strlen (cmdline_argv[i]) + 3;
      string = xmalloc (size);
      size = 0;
      for (i = 0; i < cmdline_argc; ++i)
        {
          if (i != 0)
            string[size++] = ' ';
          size += sprintf (string + size, "\"%s\"", cmdline_argv[i]);
        }
      *p = string;
      prev_inferior_args = string;
    }
  else
    *p = savestring ("", 1);	/* Initially no args */
}

void
emx_set_cmdline_args (char **argv, int argc)
{
  cmdline_argv = argv;
  cmdline_argc = argc;
}
