/*
 * Copyright (c) 2009 Charles S. Wilson
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 * OTHER DEALINGS IN THE SOFTWARE.
 */
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifndef ORIGINAL_RUN
#include <ustr.h>
#endif

#include "util.h"
#ifndef ORIGINAL_RUN
#include "confdata.h"
#endif

/* declarations of non-ANSI functions */
#if defined(__MINGW32__)
# ifdef __STRICT_ANSI__
int _putenv (const char *);
# endif
#elif defined(__CYGWIN__)
# ifdef __STRICT_ANSI__
int putenv (char *);
int setenv (const char *, const char *, int);
# endif
/* #elif defined (other platforms) ... */
#endif

#if defined (_WIN32) && !defined(__CYGWIN__)
# define PATH_SEPARATOR     ';'
# define PATH_SEPARATOR_STR ";"
#else
# define PATH_SEPARATOR     ':'
# define PATH_SEPARATOR_STR ":"
#endif

static const char * KNOWN_PATHVARS[] = {
  "PATH",
  "LD_LIBRARY_PATH",
  NULL
};

const char *
safe_strprt(const char *p)
{
  return (p ? (*p ? p : "<EMPTY>") : "<NULL>");
}

void
run2_setenv (const char *name, const char *value)
{
  debugMsg (2, "(%s) setting '%s' to '%s'", __func__,
            safe_strprt(name), safe_strprt(value));
  {
#ifdef HAVE_SETENV
    /* always make a copy, for consistency with !HAVE_SETENV */
    char *str = run2_strdup (value);
    setenv (name, str, 1);
#else
    int len = strlen (name) + 1 + strlen (value) + 1;
    char *str = (char *) run2_malloc (len);
    sprintf (str, "%s=%s", name, value);
    if (putenv (str) != EXIT_SUCCESS)
      {
        free (str);
      }
#endif
  }
}

void
run2_env_prepend (const char *name, const char *value, const char *sep)
{
  char *new_value = NULL;
  char *tmp_value = NULL;
  char *old_value = NULL;
  debugMsg (4, "(%s) Enter [name=%s, value=%s, sep=%s]", __func__,
            safe_strprt(name), safe_strprt(value), safe_strprt(sep));
  if (!name || !*name) return;
  if (!value || !*value) return;
  old_value = getenv (name);
  if (sep && *sep && old_value && *old_value)
    {
      tmp_value = run2_extend_str (old_value, sep, 0);
      new_value = run2_extend_str (tmp_value, value, 0);
      free (tmp_value);
    }
  else
    {
      new_value = run2_extend_str (old_value, value, 0);
    }
  run2_setenv (name, new_value);
  free (new_value);
}

void
run2_env_append (const char *name, const char *value, const char *sep)
{
  char *new_value = NULL;
  char *tmp_value = NULL;
  char *old_value = NULL;
  debugMsg (4, "(%s) Enter [name=%s, value=%s, sep=%s]", __func__,
            safe_strprt(name), safe_strprt(value), safe_strprt(sep));
  if (!name || !*name) return;
  if (!value || !*value) return;
  old_value = getenv (name);
  if (sep && *sep && old_value && *old_value)
    {
      tmp_value = run2_extend_str (old_value, sep, 1);
      new_value = run2_extend_str (tmp_value, value, 1);
      free (tmp_value);
    }
  else
    {
      new_value = run2_extend_str (old_value, value, 1);
    }
  run2_setenv (name, new_value);
  free (new_value);
}

static int
is_known_pathvar (const char *name)
{
  const char ** p;
  if (!name || !*name) return 0;
  for (p = KNOWN_PATHVARS; *p != NULL; p++)
    if (strcmp (*p, name) == 0)
      return 1;
  return 0;
}

#ifndef ORIGINAL_RUN
void
run2_apply_env_spec (const char *name, const run2_env_spec_t * envspec)
{
  debugMsg (4, "(%s) Enter [name=%s, envspec=%p]", __func__, safe_strprt(name), envspec);
  if (!envspec) return;
  if (envspec->set && ustr_len (envspec->set) > 0)
    {
      run2_setenv (name, ustr_cstr (envspec->set));
    }
  if (envspec->prepend && cdsl_dlist_size (envspec->prepend) > 0)
    {
      cdsl_dlist_node_t *p;
      for (p = cdsl_dlist_begin (envspec->prepend);
           p != cdsl_dlist_end (envspec->prepend);
           p = cdsl_dlist_next (p))
        {
          if (is_known_pathvar (name))
            run2_env_prepend (name, ustr_cstr ((Ustr *)cdsl_dlist_value (p)), PATH_SEPARATOR_STR);
          else
            run2_env_prepend (name, ustr_cstr ((Ustr *)cdsl_dlist_value (p)), NULL);
        }
   }
  if (envspec->append && cdsl_dlist_size (envspec->append) > 0)
    {
      cdsl_dlist_node_t *p;
      for (p = cdsl_dlist_begin (envspec->append);
           p != cdsl_dlist_end (envspec->append);
           p = cdsl_dlist_next (p))
        {
          if (is_known_pathvar (name))
            run2_env_append (name, ustr_cstr ((Ustr *)cdsl_dlist_value (p)), PATH_SEPARATOR_STR);
          else
            run2_env_append (name, ustr_cstr ((Ustr *)cdsl_dlist_value (p)), NULL);
        }
   }
}

void
run2_apply_env (const run2_env_t * env)
{
  debugMsg (4, "(%s) Enter [env=%p]", __func__, env);
  if (env->map && !cdsl_map_empty (env->map))
    {
      cdsl_map_iter_t * iter = cdsl_map_iter_new (env->map);
      if (!iter)
        {
          errorMsg ("(run2_apply_env[%d]): %s", __LINE__, strerror(errno));
          exit (1);
        }
      cdsl_map_first (env->map, iter);
      while (!cdsl_map_iter_end (iter))
        {
          const Ustr *var = (const Ustr *) cdsl_map_key (iter);
          const run2_env_spec_t *spec =
              (const run2_env_spec_t *) cdsl_map_value (iter);
          run2_apply_env_spec (ustr_cstr (var), spec);
          cdsl_map_next (iter);
        }
      cdsl_map_iter_delete (iter);
    }
}

void /* choose = 0: global, 1: gdi, 2: x11 */
run2_env (const run2_confdata_t * data, int choose)
{
  const run2_tgtopts_t * tgt = NULL;
  debugMsg (4, "(%s) Enter [data=%p, choose=%d]", __func__, data, choose);
  if (!data) return;

  /* always do global env block if it exists */
  run2_global_t * global = data->global;
  if (!global || !global->env) return;
  run2_apply_env (global->env);

  switch (choose)
    {
      case 0: /* do nothing else */ break;
      case 1:
        tgt = data->gdi;
        if (!tgt || !tgt->env) break;
        run2_apply_env (tgt->env);
        break;
      case 2:
        tgt = data->x11;
        if (!tgt || !tgt->env) break;
        run2_apply_env (tgt->env);
        break;
    }
}
#endif /* ORIGINAL_RUN */

