#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <direct.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <errno.h>

#include "evil_private.h"

/*
 * Environment variable related functions
 *
 * int setenv (const char *name, const char *value, int overwrite);
 * void unsetenv (const char *name);
 *
 */

int
setenv(const char *name,
       const char *value,
       int         overwrite)
{
   char  *old_name;
   char  *str;
   size_t length;
   int    res;

   if (!name || !*name)
     return -1;

   /* if '=' is found, return EINVAL */
   if (strchr (name, '='))
     {
        errno = EINVAL;
        return -1;
     }

   /* if name is already set and overwrite is 0, we exit with success */
   old_name = getenv(name);
   if (!overwrite && old_name)
     return 0;

   length = value ? strlen(value) : 0;
   length += strlen(name) + 2;
   str = (char *)malloc(length);
   if (!str)
     {
        errno = ENOMEM;
        return -1;
     }
   if (!value)
     sprintf(str, "%s=", name);
   else
     sprintf(str, "%s=%s", name, value);
   res = _putenv(str);
   free(str);

   return res;
}

int
unsetenv(const char *name)
{
   return setenv(name, NULL, 1);
}


/*
 * Files related functions
 *
 */
static int
_mkstemp_init(char *__template, char **suffix, size_t *length, DWORD *val,
              size_t suffixlen)
{
   *length = strlen(__template);
   if ((*length < (6 + suffixlen))
       || (strncmp(__template + *length - 6 - suffixlen, "XXXXXX", 6) != 0))
     {
        errno = EINVAL;
        return 0;
     }

   *suffix = __template + *length - 6 - suffixlen;

   *val = GetTickCount();
   *val += GetCurrentProcessId();

   return 1;
}

static int
_mkstemp(char *suffix, int val)
{
   const char lookup[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
   DWORD v = val;

   suffix[0] = lookup[v % 62];
   v /= 62;
   suffix[1] = lookup[v % 62];
   v /= 62;
   suffix[2] = lookup[v % 62];
   v /= 62;
   suffix[3] = lookup[v % 62];
   v /= 62;
   suffix[4] = lookup[v % 62];
   v /= 62;
   suffix[5] = lookup[v % 62];

   val += 7777;

   return val;
}

EAPI char *
mkdtemp(char *__template)
{
   char      *suffix;
   DWORD      val;
   size_t     length;
   int        i;

   if (!__template)
     {
        errno = EINVAL;
        return NULL;
     }

   if (!_mkstemp_init(__template, &suffix, &length, &val, 0))
     return NULL;

   for (i = 0; i < 32768; i++)
     {
        val = _mkstemp(suffix, val);

        if (_mkdir(__template) == 0)
          return __template;

        if (errno == EFAULT ||
            errno == ENOSPC ||
            errno == ENOMEM ||
            errno == ENOENT ||
            errno == ENOTDIR ||
            errno == EPERM ||
            errno == EROFS)
          return NULL;
     }

   errno = EEXIST;
   return NULL;
}

int
mkstemps(char *__template, int suffixlen)
{
   char      *suffix;
   DWORD      val;
   size_t     length;
   int        i;

   if (!__template || (suffixlen < 0))
     return 0;

   if (!_mkstemp_init(__template, &suffix, &length, &val, (size_t) suffixlen))
     return -1;

   for (i = 0; i < 32768; i++)
     {
        int fd;

        val = _mkstemp(suffix, val);

        fd = _open(__template, _O_RDWR | _O_BINARY | _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
        if (fd >= 0)
          return fd;
     }

   errno = EEXIST;
   return -1;
}

char *
realpath(const char *file_name, char *resolved_name)
{
   char *retname = NULL;  /* we will return this, if we fail */

   /* SUSv3 says we must set `errno = EINVAL', and return NULL,
    * if `name' is passed as a NULL pointer.
    */

   if (file_name == NULL)
     errno = EINVAL;

   /* Otherwise, `name' must refer to a readable filesystem object,
    * if we are going to resolve its absolute path name.
    */

   else if (access(file_name, 4) == 0)
     {
        /* If `name' didn't point to an existing entity,
         * then we don't get to here; we simply fall past this block,
         * returning NULL, with `errno' appropriately set by `access'.
         *
         * When we _do_ get to here, then we can use `_fullpath' to
         * resolve the full path for `name' into `resolved', but first,
         * check that we have a suitable buffer, in which to return it.
         */

       if ((retname = resolved_name) == NULL)
         {
            /* Caller didn't give us a buffer, so we'll exercise the
             * option granted by SUSv3, and allocate one.
             *
             * `_fullpath' would do this for us, but it uses `malloc', and
             * Microsoft's implementation doesn't set `errno' on failure.
             * If we don't do this explicitly ourselves, then we will not
             * know if `_fullpath' fails on `malloc' failure, or for some
             * other reason, and we want to set `errno = ENOMEM' for the
             * `malloc' failure case.
             */

           retname = malloc(_MAX_PATH);
         }

       /* By now, we should have a valid buffer.
        * If we don't, then we know that `malloc' failed,
        * so we can set `errno = ENOMEM' appropriately.
        */

       if (retname == NULL)
         errno = ENOMEM;

       /* Otherwise, when we do have a valid buffer,
        * `_fullpath' should only fail if the path name is too long.
        */

       else if ((retname = _fullpath(retname, file_name, _MAX_PATH)) == NULL)
         errno = ENAMETOOLONG;
     }

   /* By the time we get to here,
    * `retname' either points to the required resolved path name,
    * or it is NULL, with `errno' set appropriately, either of which
    * is our required return condition.
    */

   return retname;
}
