#include <config.h>

#if defined (__DJGPP__)

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <process.h>
#include <sys/system.h>

#include "command.h"
#include "general.h"
#include "externs.h"
#include "dospath.h"

static char *spawn_make_response_file (char **argv);
static void reset_console (void);

static const char * dos_ext_names[] =
{ "exe", "bat", "com", "btm" };

int bash_spawnve(const char *path, char **args, char **envp)
{
  int status;
  int fd;
  char oldcwd[PATH_MAX];
  int errno_save;
  int path_len;
  int i, is_dos_ext;
  int *selectors;
  int old_enable, old_ctrl_c;
  const _v2_prog_type *type;
  char *base, *dot;

  /* Force scripts not starting with #! to be invoked with Bash.  */
  is_dos_ext = 0;
  base = basename (path);
  dot = strchr (base, '.');
  
  if (dot && dot[1] != '\0')
    {
      ++dot;
      for (i = 0; i < sizeof (dos_ext_names) / sizeof (dos_ext_names[0]); ++i)
        {
          if (strcmp (dot, dos_ext_names[i]) == 0)
            {
              is_dos_ext = 1;
              break;
            }
        }
    }

  if (!is_dos_ext)
    {
      char sample[2];
      int fd = open (path, O_RDONLY | O_BINARY);
      if (fd >= 0)
        {
          int sample_len = read (fd, sample, 2);
          close (fd);
          if (sample_len == 2)
            {
              if (sample[0] == 0x4c || sample[1] == 0x01)
                {
                  /* Unstubbed COFF. Check for a file with .exe and
                     use it if it exists.  */
                  const char *exe_path = find_one_extension (path, "exe");
                  if (exe_path)
                    path = exe_path;
                }
              else if (!(sample[0] == 'M' || sample[1] == 'Z')
                       && !(sample[0] == '#' || sample[1] == '!'))
                {
                  /* Let Bash insert the shell name into argv[0] so
                     scripts without #! will run.  */
                   errno = ENOEXEC;
                   return -1;
                }
            }
        }
    }

  /* keep cwd on exec */
  getcwd (oldcwd, sizeof oldcwd);

  /* Expand 'path'.  */
  if (strncmp(path, "/dev/", sizeof("/dev/") - 1) == 0)
    path = get_real_path (path);

  /* disable interrupt */
  old_ctrl_c = __djgpp_set_ctrl_c (0);

  errno = 0;

  /* Run child process.  */
  status = __djgpp_spawn (P_WAIT, path, args, envp, SPAWN_NO_EXTENSION_SRCH);

  /* make response file if E2BIG */
  if (errno == E2BIG && args != NULL && args[1] != NULL && args[1][0] != '@')
    {
      char *tname;

      tname = spawn_make_response_file (args);
      if (tname != NULL)
        {
          char *xargs[3];

          xargs[0] = args[0];
          xargs[1] = xmalloc (1 + strlen (tname) + 2);
          if (xargs[1])
            {
              char *resp = xargs[1];
              resp[0] = '@';
              strcpy (resp + 1, tname);

              xargs[2] = NULL;

              errno = 0;

              /* try again */
              status = __djgpp_spawn (P_WAIT, path, xargs, envp,
                                      SPAWN_NO_EXTENSION_SRCH);

              /* cleanup */
              free (xargs[1]);
              remove (tname);
              free (tname);
            }
        }
    }

  /* Reset standard input */
#if 0
  reset_console();
#endif

  /* enable interrupt */
  __djgpp_set_ctrl_c (old_ctrl_c);

  errno_save = errno;
  if (status >= 0)
    chdir (oldcwd);
  errno = errno_save;

  return status;
}

static char *
spawn_make_response_file (char **argv)
{
  char *tname, *name_ptr;

  tname = sh_mktmpname ("rs", MT_USERANDOM | MT_USETMPDIR);

  if (tname != NULL)
    {
      FILE *fp;
      char *p;
      int i;
      
      fp = fopen (tname, "wt");
      if (fp == NULL)
        {
          free (tname);
          return NULL;
        }

      for (i = 1; (p = argv[i]) != NULL; i++)
        {
          if (i != 1)
            fputc (' ', fp);
          if (strchr (p, '"') == NULL)
            {
              fputc ('"', fp);
              fputs (p, fp);
              fputc ('"', fp);
            }
          else
            {
              fputc ('"', fp);
              while (*p)
                {
                  if (*p == '"')
                    fputc ('\\', fp);
                  fputc (*p, fp);
                  p++;
                }
              fputc ('"', fp);
            }
        }
      fclose (fp);
    }

  return tname;
}

#if 0
#include <libc/getdinfo.h>

#define _DEV_EOF    0x0040

#define _REG_STATUS_CF 0x01
#define _REG_STATUS_ZF 0x40

/* XXX: Is this needed? */
void
reset_console (void)
{
  unsigned short devinfo;
  int handle;
  __dpmi_regs r;

  handle = 0; /* STDIN (CON) */

  r.x.ax = 0x4400;
  r.x.bx = handle;
  __dpmi_int (0x21, &r);
  if (r.x.flags & _REG_STATUS_CF)
    return;
  devinfo = r.x.dx;

  if ((devinfo & _DEV_CDEV) && (devinfo & _DEV_STDIN) && (devinfo & _DEV_EOF) == 0)
    {
      r.x.ax = 0x4000; /* WRITE */
      r.x.bx = handle; /* STDIN (CON) */
      r.x.cx = 0; /* zero byte */
      r.x.dx = 0; /* dummy offset */
      r.x.ds = 0; /* dummy segment */
      __dpmi_int(0x21, &r);
      if (r.x.flags & _REG_STATUS_CF)
	return;
    }
}
#endif

#endif
