#ifdef __MSDOS__

#include <ctype.h>
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <io.h>

#include "config.h"
#include "command.h"
#include "general.h"
#include "error.h"
#include "variables.h"
#include "subst.h"
#include "externs.h"
#include "builtins/common.h"
#include "dosutil.h"
#include "hashcmd.h"
#include "bashjmp.h"

#define SAVESTRING(p)  ((p != NULL) ? savestring (p) : NULL)
#define FREE_AND_CLEAR(p)  (free (p), p = NULL)

#define CURRENT_PID ((pid_t) 1)

pid_t dosutil_current_pid = CURRENT_PID;
int dosutil_wait_status = -1;

static void
save_jmp_buf (jmp_buf save, jmp_buf now)
{
  memcpy ((char *) save, (char *) now, sizeof (jmp_buf));
}

static void
restore_jmp_buf (jmp_buf save, jmp_buf now)
{
  memcpy ((char *) now, (char *) save, sizeof (jmp_buf));
}

extern jmp_buf top_level, subshell_top_level;
extern int interactive, interactive_shell, login_shell;
extern int subshell_environment;
extern int subshell_exit_builtin, exit_immediately_on_error;
extern int dollar_dollar_pid;
extern int array_needs_making;
extern char **temporary_env, **function_env, **builtin_env;

static void
save_current_directory (OLDENVBUF *envp)
{
  envp->pwd    = SAVESTRING (get_string_value ("PWD"));
#if 0
  envp->oldpwd = SAVESTRING (get_string_value ("OLDPWD"));
#endif
  envp->curdir = getcwd (NULL, PATH_MAX);
}

static void
restore_current_directory (OLDENVBUF *envp)
{
  /* change old directory */
  if (envp->curdir != NULL)
    {
      chdir (envp->curdir);
      if (envp->pwd != NULL)
	set_working_directory (envp->pwd);
      else
	set_working_directory (envp->curdir);
      FREE_AND_CLEAR (envp->curdir);
    }
  else if (envp->pwd != NULL)
    {
      chdir (envp->pwd);
      set_working_directory (envp->pwd);
    }

#if 0
  /* replace variables */
  unbind_variable ("PWD");
  if (envp->pwd)
    {
      bind_variable ("PWD", envp->pwd);
      FREE_AND_CLEAR (envp->pwd);
    }

  unbind_variable ("OLDPWD");
  if (envp->oldpwd)
    {
      bind_variable ("OLDPWD", envp->oldpwd);
      FREE_AND_CLEAR (envp->oldpwd);
    }
#else
  if (envp->pwd)
    FREE_AND_CLEAR (envp->pwd);
#endif
}

extern WORD_LIST *subst_assign_varlist;

static void
save_global_variables (OLDENVBUF *envp)
{
  envp->interactive               = interactive;
  envp->interactive_shell         = interactive_shell;
  envp->login_shell               = login_shell;
  envp->subshell_environment      = subshell_environment;
  envp->subshell_exit_builtin     = subshell_exit_builtin;
  envp->exit_immediately_on_error = exit_immediately_on_error;
  envp->variable_context          = variable_context;
  envp->dollar_dollar_pid         = dollar_dollar_pid;
  envp->subst_assign_varlist      = subst_assign_varlist;
  subst_assign_varlist = NULL;
}

static void
restore_global_variables (OLDENVBUF *envp)
{
  interactive               = envp->interactive;
  interactive_shell         = envp->interactive_shell;
  login_shell               = envp->login_shell;
  subshell_environment      = envp->subshell_environment;
  subshell_exit_builtin     = envp->subshell_exit_builtin;
  exit_immediately_on_error = envp->exit_immediately_on_error;
  variable_context          = envp->variable_context;
  dollar_dollar_pid         = envp->dollar_dollar_pid;
  subst_assign_varlist      = envp->subst_assign_varlist;
}

static HASH_TABLE *
copy_shell_variables_hash_table (HASH_TABLE *table)
{
  int i;
  HASH_TABLE *new_table;
  BUCKET_CONTENTS *new_item, *old_item;
  SHELL_VAR *new_var, *old_var;

  if (!table)
    return (HASH_TABLE *) NULL;

  new_table = make_hash_table (table->nbuckets);
  new_table->nentries = table->nentries;
  for (i = 0; i < table->nbuckets; i++)
    {
      old_item = table->bucket_array[i];
      new_item = (BUCKET_CONTENTS *) NULL;
      while (old_item)
	{
	  if (new_item)
	    {
	      new_item->next = (BUCKET_CONTENTS *) xmalloc (sizeof (BUCKET_CONTENTS));
	      new_item = new_item->next;
	    }
	  else
	    {
	      new_table->bucket_array[i] =
		(BUCKET_CONTENTS *) xmalloc (sizeof (BUCKET_CONTENTS));
	      new_item = new_table->bucket_array[i];
	    }
	  new_item->data = (char *) copy_variable ((SHELL_VAR *) old_item->data);
	  old_var = (SHELL_VAR *)old_item->data;
	  new_var = (SHELL_VAR *)new_item->data;
	  while (old_var->prev_context)
	    {
	      new_var->prev_context = copy_variable (old_var->prev_context);
	      new_var = new_var->prev_context;
	      old_var = old_var->prev_context;
	    }
	  new_item->next = (BUCKET_CONTENTS *)NULL;
	  new_item->key = SAVESTRING (old_item->key);
	  new_item->times_found = old_item->times_found;
	  old_item = old_item->next;
	}
    }

  return new_table;
}

static void
dispose_shell_variables_hash_table (HASH_TABLE *table)
{
  int i;
  BUCKET_CONTENTS *item, *next;
  SHELL_VAR *var, *prev;

  if (!table)
    return;

  for (i = 0; i < table->nbuckets; i++)
    {
      item = table->bucket_array[i];
      while (item)
	{
	  var = (SHELL_VAR *) item->data;
	  while (var)
	    {
	      prev = var->prev_context;
	      dispose_variable (var);
	      var = prev;
	    }
	  if (item->key)
	    free (item->key);
	  next = item->next;
	  free (item);
	  item = next;
	}
    }
  free (table->bucket_array);
  free (table);
}

static void
save_shell_variables (OLDENVBUF *envp)
{
  maybe_make_export_env ();

  envp->shell_variables = shell_variables;
  shell_variables = copy_shell_variables_hash_table (shell_variables);
  envp->shell_functions = shell_functions;
  shell_functions = copy_shell_variables_hash_table (shell_functions);

  envp->temporary_env = temporary_env;
  if (temporary_env != NULL)
    temporary_env = copy_array (temporary_env);
  envp->function_env = function_env;
  if (function_env != NULL)
    function_env = copy_array (function_env);
  envp->builtin_env = builtin_env;
  if (builtin_env != NULL)
    builtin_env = copy_array (builtin_env);
#if 0
  /* made by maybe_make_export_env () */
  envp->export_env = export_env;
  if (export_env != NULL)
    export_env = copy_array (export_env);
#endif

  envp->rest_of_args = list_rest_of_args ();
}

static void
restore_shell_variables (OLDENVBUF *envp)
{
  dispose_shell_variables_hash_table (shell_variables);
  shell_variables = envp->shell_variables;
  dispose_shell_variables_hash_table (shell_functions);
  shell_functions = envp->shell_functions;

  free_array (temporary_env);
  temporary_env = envp->temporary_env;
  free_array (function_env);
  function_env = envp->function_env;
  free_array (builtin_env);
  builtin_env = envp->builtin_env;
#if 0
  /* made by maybe_make_export_env () */
  free_array (export_env);
  export_env = envp->export_env;
#endif

  remember_args (envp->rest_of_args, 1);

  array_needs_making = 1;
  maybe_make_export_env ();
}

extern int return_catch_flag;

int
dosutil_save_all_environment (OLDENVBUF *envp)
{
  save_jmp_buf (envp->top_level, top_level);
  save_jmp_buf (envp->subshell_top_level, subshell_top_level);
  save_jmp_buf (envp->return_catch, return_catch);
  envp->return_catch_flag = return_catch_flag;
  dosutil_save_std_fds (envp->fds);
  save_current_directory (envp);
  save_global_variables (envp);
  save_shell_variables (envp);
  return 0;
}

int
dosutil_restore_all_environment (OLDENVBUF *envp)
{
  restore_shell_variables (envp);
  restore_global_variables (envp);
  restore_current_directory (envp);
  dosutil_restore_std_fds (envp->fds);
  return_catch_flag = envp->return_catch_flag;
  restore_jmp_buf (envp->return_catch, return_catch);
  restore_jmp_buf (envp->top_level, top_level);
  restore_jmp_buf (envp->subshell_top_level, subshell_top_level);
  return 0;
}
#include <sys/param.h>
#if !defined (MAXPID) 
#define MAXPID ((pid_t) 30000)
#endif

static pid_t last_pid = CURRENT_PID;

pid_t
dosutil_make_child_pid (void)
{
  if (++last_pid > MAXPID)
    last_pid = CURRENT_PID + 1;

  return last_pid;
}

#endif

