#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <process.h>

#ifdef OS2
#ifdef _MSC_VER
#define popen(c,m)   _popen(c,m)
#define pclose(f)    _pclose(f)
#endif
#endif

#if defined(WIN32) && defined(_MSC_VER)
#define popen _popen
#define pclose _pclose
#endif

#ifndef _NFILE
#define _NFILE 40
#endif

static char template[] = "piXXXXXX";
static struct {
  char *command;
  char *name;
  char pmode[4];
} pipes[_NFILE];


/*
 * For systems where system() and popen() do not follow SHELL:
 *  1. Write command to temp file.  Temp filename must have slashes
 *     compatible with SHELL (if set) or COMSPEC.
 *  2. Convert slashes in SHELL (if present) to be compatible with COMSPEC.
 * Currently, only MSC (running under DOS) and MINGW versions are managed.
 */

#if defined(_MSC_VER) || defined(__MINGW32__)

static int
unixshell(char *p)
{
  static char *shell[] = {"sh", "bash", "csh", "tcsh", "sh32", "sh16", "ksh", NULL};
  char **shellp = shell, *s, *q;

  if (p == NULL) return (0);
  s = p = strdup(p);
  if ((q = strrchr(p, '\\')) != NULL)
    p = q + 1;
  if ((q = strrchr(p, '/')) != NULL)
    p = q + 1;
  if ((q = strchr(p, '.')) != NULL)
    *q = '\0';
  strlwr(p);
  do {
    if (strcmp(*shellp, p) == 0) break;
  } while (*++shellp);
  free(s);
  return(*shellp ? 1 : 0);
}

static char *
slashify(char *p, char *s)
{
  if (unixshell(s))
    while (s = strchr(p, '\\')) *s = '/';
  else
    while (s = strchr(p, '/')) *s = '\\';
  return(p);
}

static char *
scriptify(const char *command)
{
  FILE *fp;
  char *cmd, *name, *s, *p;
  int i;

  if((name = tempnam(".", "pip")) == NULL) return(NULL);
  p = getenv("COMSPEC"); s = getenv("SHELL");
  cmd = malloc(strlen(name) + (s ? strlen(s) : 0) + 9); *cmd = '\0';
  if (s) {
    slashify(strcpy(cmd, s), p);
    p = s;
  }
  slashify(name, p);
  if (! (i = unixshell(p))) {
    char *p = (char *) realloc(name, strlen(name) + 5);
    if (p == NULL)
	return NULL;
    name = p;
    strcat(name, ".bat");
  }
  if (s) sprintf(cmd + strlen(cmd), " %cc ", unixshell(s) ? '-' : '/');
  strcpy(p = cmd + strlen(cmd), name); free(name);

  if ((fp = fopen(p, i ? "wb" : "w")) != NULL) {
    if (! i) fputs("@echo off\n", fp);
    i = strlen(command);
    if ((fwrite(command, 1, i, fp) < i) || (fputc('\n', fp) == EOF))
      cmd = NULL; 
  } else
    cmd = NULL;
  if (fp) fclose(fp); 
  return(cmd);
}

static void
unlink_and_free(char *cmd)
{
  char *s;

  if (s = strrchr(cmd, ' '))
    s++;
  else
    s = cmd;
  unlink(s); free(cmd);
}

int
os_system(const char *cmd)
{
  char *s;
  int i;
  char *cmd1;

#if defined(OS2)
  if (_osmode == OS2_MODE)
    return(system(cmd));
#endif

  if ((cmd1 = scriptify(cmd)) == NULL) return(1);
  if (s = getenv("SHELL"))
    i = spawnlp(P_WAIT, s, s, cmd1 + strlen(s), NULL);
  else
    i = system(cmd1);
  unlink_and_free(cmd1);
  return(i);
}
#else
#define os_system(cmd) system(cmd)
#endif


FILE *
os_popen(const char *command, char *mode )
{
    FILE *current;
    char *name;
    int cur;
    char curmode[4];
#if defined(__MINGW32__) || (defined(_MSC_VER) && defined(WIN32))
    char *cmd;
#endif
    
#if defined(OS2) && (_MSC_VER != 510)
    if (_osmode == OS2_MODE)
      return(popen(command, mode));
#endif

    if (*mode != 'r' && *mode != 'w')
      return NULL;
    strncpy(curmode, mode, 3); curmode[3] = '\0';

#if defined(__MINGW32__) || (defined(_MSC_VER) && defined(WIN32))
    current = popen(cmd = scriptify(command), mode);
    cur = fileno(current);
    strcpy(pipes[cur].pmode, curmode);
    pipes[cur].command = cmd;
    return(current);
#endif

    /*
    ** get a name to use.
    */
    if((name = tempnam(".","pip"))==NULL)
        return NULL;
    /*
    ** If we're reading, just call system to get a file filled with
    ** output.
    */
    if (*curmode == 'r') {
        FILE *fp;
        if ((cur = dup(fileno(stdout))) == -1)
	    return NULL;
	*curmode = 'w';
        if ((current = freopen(name, curmode, stdout)) == NULL)
	    return NULL;
        os_system(command);
        if (dup2(cur, fileno(stdout)) == -1)
	    return NULL;
	close(cur);
	*curmode = 'r';
        if ((current = fopen(name, curmode)) == NULL)
            return NULL;
    } else {
        if ((current = fopen(name, curmode)) == NULL)
            return NULL;
    }
    cur = fileno(current);
    pipes[cur].name = name;
    strcpy(pipes[cur].pmode, curmode);
    pipes[cur].command = strdup(command);
    return current;
}

int
os_pclose( FILE * current)
{
    int cur = fileno(current);
    int fd, rval;

#if defined(OS2) && (_MSC_VER != 510)
    if (_osmode == OS2_MODE)
      return(pclose(current));
#endif

#if defined(__MINGW32__) || (defined(_MSC_VER) && defined(WIN32))
    rval = pclose(current);
    *pipes[cur].pmode = '\0';
    unlink_and_free(pipes[cur].command);
    return rval;
#endif

    /*
    ** check for an open file.
    */
    switch (*pipes[cur].pmode) {
    case 'r':
        /*
        ** input pipes are just files we're done with.
        */
        rval = fclose(current);
        unlink(pipes[cur].name);
	break;
    case 'w':
        /*
        ** output pipes are temporary files we have
        ** to cram down the throats of programs.
        */
        fclose(current);
	rval = -1;
	if ((fd = dup(fileno(stdin))) != -1) {
	  char *mode = pipes[cur].pmode; *mode = 'r';
	  if (current = freopen(pipes[cur].name, mode, stdin)) {
	    rval = os_system(pipes[cur].command);
	    fclose(current);
	    if (dup2(fd, fileno(stdin)) == -1) rval = -1;
	    close(fd);
	  }
	}
        unlink(pipes[cur].name);
	break;
    default:
      return -1;
    }
    /*
    ** clean up current pipe.
    */
    *pipes[cur].pmode = '\0';
    free(pipes[cur].name);
    free(pipes[cur].command);
    return rval;
}
