#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 <sys/exceptn.h>

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

static char *spawn_make_response_file(char **argv);
#if 0
static void reset_console(void);
#endif /* 0 */

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

int bash_spawnve(const char *path, char **args, char **envp)
{
  int status;
  char oldcwd[PATH_MAX];
  int errno_save;
  int i, is_dos_ext;
  int old_ctrl_c;
  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 > -1)
    {
      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 /* 0 */

  /* enable interrupt */
  __djgpp_set_ctrl_c(old_ctrl_c);

  errno_save = errno;
  if (status > -1)
    chdir(oldcwd);
  errno = errno_save;

  return status;
}

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

  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);
        for (; *p; p++)
        {
          if (*p == '"')
            fputc('\\', fp);
          fputc(*p, fp);
        }
        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 /* 0 */

#endif /* __DJGPP__ */
