/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  If you do not have          *
 * access to either file, you may request a copy from help@hdfgroup.org.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*-------------------------------------------------------------------------
 *
 * Created:    H5system.c
 *      Aug 21 2006
 *      Quincey Koziol <koziol@hdfgroup.org>
 *
 * Purpose:    System call wrapper implementations.
 *
 *-------------------------------------------------------------------------
 */

/****************/
/* Module Setup */
/****************/


/***********/
/* Headers */
/***********/
#include "H5private.h"		/* Generic Functions			*/
#include "H5Eprivate.h"		/* Error handling		  	*/
#include "H5Fprivate.h"		/* File access				*/
#include "H5MMprivate.h"	/* Memory management			*/


/****************/
/* Local Macros */
/****************/


/******************/
/* Local Typedefs */
/******************/


/********************/
/* Package Typedefs */
/********************/


/********************/
/* Local Prototypes */
/********************/


/*********************/
/* Package Variables */
/*********************/


/*****************************/
/* Library Private Variables */
/*****************************/


/*******************/
/* Local Variables */
/*******************/

/* Track whether tzset routine was called */
static hbool_t H5_ntzset = FALSE;


/*-------------------------------------------------------------------------
 * Function:  HDfprintf
 *
 * Purpose:  Prints the optional arguments under the control of the format
 *    string FMT to the stream STREAM.  This function takes the
 *    same format as fprintf(3c) with a few added features:
 *
 *    The conversion modifier `H' refers to the size of an
 *    `hsize_t' or `hssize_t' type.  For instance, "0x%018Hx"
 *    prints an `hsize_t' value as a hex number right justified and
 *    zero filled in an 18-character field.
 *
 *    The conversion `a' refers to an `haddr_t' type.
 *
 * Return:  Success:  Number of characters printed
 *
 *    Failure:  -1
 *
 * Programmer:  Robb Matzke
 *              Thursday, April  9, 1998
 *
 * Modifications:
 *    Robb Matzke, 1999-07-27
 *    The `%a' format refers to an argument of `haddr_t' type
 *    instead of `haddr_t*' and the return value is correct.
 *-------------------------------------------------------------------------
 */
int
HDfprintf(FILE *stream, const char *fmt, ...)
{
    int    n=0, nout = 0;
    int    fwidth, prec;
    int    zerofill;
    int    leftjust;
    int    plussign;
    int    ldspace;
    int    prefix;
    char  modifier[8];
    int    conv;
    char  *rest, format_templ[128];
    int    len;
    const char  *s;
    va_list  ap;

    HDassert(stream);
    HDassert(fmt);

    va_start (ap, fmt);
    while (*fmt) {
        fwidth = prec = 0;
        zerofill = 0;
        leftjust = 0;
        plussign = 0;
        prefix = 0;
        ldspace = 0;
        modifier[0] = '\0';

        if ('%'==fmt[0] && '%'==fmt[1]) {
            HDputc ('%', stream);
            fmt += 2;
            nout++;
        } else if ('%'==fmt[0]) {
            s = fmt + 1;

            /* Flags */
            while(HDstrchr("-+ #", *s)) {
                switch(*s) {
                    case '-':
                        leftjust = 1;
                        break;

                    case '+':
                        plussign = 1;
                        break;

                    case ' ':
                        ldspace = 1;
                        break;

                    case '#':
                        prefix = 1;
                        break;

                    default:
                        HDassert(0 && "Unknown format flag");
                } /* end switch */ /*lint !e744 Switch statement doesn't _need_ default */
                s++;
            } /* end while */

            /* Field width */
            if(HDisdigit(*s)) {
                zerofill = ('0' == *s);
                fwidth = (int)HDstrtol (s, &rest, 10);
                s = rest;
            } /* end if */
            else if ('*'==*s) {
                fwidth = va_arg (ap, int);
                if(fwidth < 0) {
                    leftjust = 1;
                    fwidth = -fwidth;
                }
                s++;
            }

            /* Precision */
            if('.'==*s) {
                s++;
                if(HDisdigit(*s)) {
                    prec = (int)HDstrtol(s, &rest, 10);
                    s = rest;
                } else if('*'==*s) {
                    prec = va_arg(ap, int);
                    s++;
                }
                if(prec < 1)
                    prec = 1;
            }

            /* Extra type modifiers */
            if(HDstrchr("zZHhlqLI", *s)) {
                switch(*s) {
                    /*lint --e{506} Don't issue warnings about constant value booleans */
                    /*lint --e{774} Don't issue warnings boolean within 'if' always evaluates false/true */
                    case 'H':
                        if(sizeof(hsize_t) < sizeof(long))
                            modifier[0] = '\0';
                        else if(sizeof(hsize_t) == sizeof(long)) {
                            HDstrncpy(modifier, "l", sizeof(modifier));
                            modifier[sizeof(modifier) - 1] = '\0';
                        } /* end if */
                        else {
                            HDstrncpy(modifier, H5_PRINTF_LL_WIDTH, sizeof(modifier));
                            modifier[sizeof(modifier) - 1] = '\0';
                        } /* end else */
                        break;

                    case 'Z':
                    case 'z':
                        if(sizeof(size_t) < sizeof(long))
                            modifier[0] = '\0';
                        else if(sizeof(size_t) == sizeof(long)) {
                            HDstrncpy(modifier, "l", sizeof(modifier));
                            modifier[sizeof(modifier) - 1] = '\0';
                        } /* end if */
                        else {
                            HDstrncpy(modifier, H5_PRINTF_LL_WIDTH, sizeof(modifier));
                            modifier[sizeof(modifier) - 1] = '\0';
                        } /* end else */
                        break;

                    default:
                        /* Handle 'I64' modifier for Microsoft's "__int64" type */
                        if(*s=='I' && *(s+1)=='6' && *(s+2)=='4') {
                            modifier[0] = *s;
                            modifier[1] = *(s+1);
                            modifier[2] = *(s+2);
                            modifier[3] = '\0';
                            s += 2; /* Increment over 'I6', the '4' is taken care of below */
                        } /* end if */
                        else {
                            /* Handle 'll' for long long types */
                            if(*s=='l' && *(s+1)=='l') {
                                modifier[0] = *s;
                                modifier[1] = *s;
                                modifier[2] = '\0';
                                s++; /* Increment over first 'l', second is taken care of below */
                            } /* end if */
                            else {
                                modifier[0] = *s;
                                modifier[1] = '\0';
                            } /* end else */
                        } /* end else */
                        break;
                }
                s++;
            }

            /* Conversion */
            conv = *s++;

            /* Create the format template */
            len = 0;
            len += HDsnprintf(format_templ, (sizeof(format_templ) - (size_t)(len + 1)), "%%%s%s%s%s%s", (leftjust ? "-" : ""),
                    (plussign ? "+" : ""), (ldspace ? " " : ""),
                    (prefix ? "#" : ""), (zerofill ? "0" : ""));
            if(fwidth > 0)
                len += HDsnprintf(format_templ + len, (sizeof(format_templ) - (size_t)(len + 1)), "%d", fwidth);
            if(prec > 0)
                len += HDsnprintf(format_templ + len, (sizeof(format_templ) - (size_t)(len + 1)), ".%d", prec);
            if(*modifier)
                len += HDsnprintf(format_templ + len, (sizeof(format_templ) - (size_t)(len + 1)), "%s", modifier);
            HDsnprintf(format_templ + len, (sizeof(format_templ) - (size_t)(len + 1)), "%c", conv);


            /* Conversion */
            switch (conv) {
                case 'd':
                case 'i':
                    if(!HDstrcmp(modifier, "h")) {
                        short x = (short)va_arg (ap, int);
                        n = fprintf (stream, format_templ, x);
                    } else if(!*modifier) {
                        int x = va_arg (ap, int);
                        n = fprintf (stream, format_templ, x);
                    } else if(!HDstrcmp (modifier, "l")) {
                        long x = va_arg (ap, long);
                        n = fprintf (stream, format_templ, x);
                    } else {
                        int64_t x = va_arg(ap, int64_t);
                        n = fprintf (stream, format_templ, x);
                    }
                    break;

                case 'o':
                case 'u':
                case 'x':
                case 'X':
                    if(!HDstrcmp(modifier, "h")) {
                        unsigned short x = (unsigned short)va_arg (ap, unsigned int);
                        n = fprintf(stream, format_templ, x);
                    } else if(!*modifier) {
                        unsigned int x = va_arg (ap, unsigned int); /*lint !e732 Loss of sign not really occuring */
                        n = fprintf(stream, format_templ, x);
                    } else if(!HDstrcmp(modifier, "l")) {
                        unsigned long x = va_arg (ap, unsigned long); /*lint !e732 Loss of sign not really occuring */
                        n = fprintf(stream, format_templ, x);
                    } else {
                        uint64_t x = va_arg(ap, uint64_t); /*lint !e732 Loss of sign not really occuring */
                        n = fprintf(stream, format_templ, x);
                    }
                    break;

                case 'f':
                case 'e':
                case 'E':
                case 'g':
                case 'G':
                    if(!HDstrcmp(modifier, "h")) {
                        float x = (float)va_arg(ap, double);
                        n = fprintf(stream, format_templ, (double)x);
                    } else if(!*modifier || !HDstrcmp(modifier, "l")) {
                        double x = va_arg(ap, double);
                        n = fprintf(stream, format_templ, x);
                    } else {
                    /*
                    * Some compilers complain when `long double' and
                    * `double' are the same thing.
                    */
#if H5_SIZEOF_LONG_DOUBLE != H5_SIZEOF_DOUBLE
                        long double x = va_arg(ap, long double);
                        n = fprintf(stream, format_templ, x);
#else
                        double x = va_arg(ap, double);
                        n = fprintf(stream, format_templ, x);
#endif
                    }
                    break;

                case 'a':
                    {
                        haddr_t x = va_arg (ap, haddr_t); /*lint !e732 Loss of sign not really occuring */

                        if(H5F_addr_defined(x)) {
                            len = 0;
                            len += HDsnprintf(format_templ, (sizeof(format_templ) - (size_t)(len + 1)), "%%%s%s%s%s%s",
                                (leftjust ? "-" : ""), (plussign ? "+" : ""),
                                (ldspace ? " " : ""), (prefix ? "#" : ""),
                                (zerofill ? "0" : ""));
                            if(fwidth > 0)
                                len += HDsnprintf(format_templ + len, (sizeof(format_templ) - (size_t)(len + 1)), "%d", fwidth);

                            /*lint --e{506} Don't issue warnings about constant value booleans */
                            /*lint --e{774} Don't issue warnings boolean within 'if' always evaluates false/true */
                            if(sizeof(x) == H5_SIZEOF_INT) {
                                HDstrncat(format_templ, "u", (sizeof(format_templ) - (size_t)(len + 1)));
                                len++;
                            } /* end if */
                            else if(sizeof(x) == H5_SIZEOF_LONG) {
                                HDstrncat(format_templ, "lu", (sizeof(format_templ) - (size_t)(len + 1)));
                                len++;
                            } /* end if */
                            else if(sizeof(x) == H5_SIZEOF_LONG_LONG) {
                                HDstrncat(format_templ, H5_PRINTF_LL_WIDTH, (sizeof(format_templ) - (size_t)(len + 1)));
                                len += (int)sizeof(H5_PRINTF_LL_WIDTH);
                                HDstrncat(format_templ, "u", (sizeof(format_templ) - (size_t)(len + 1)));
                                len++;
                            }
                            n = fprintf(stream, format_templ, x);
                        } else {
                            len = 0;
                            HDstrncpy(format_templ, "%", (sizeof(format_templ) - (size_t)(len + 1)));
                            len++;
                            if(leftjust) {
                                HDstrncat(format_templ, "-", (sizeof(format_templ) - (size_t)(len + 1)));
                                len++;
                            } /* end if */
                            if(fwidth)
                                len += HDsnprintf(format_templ + len, (sizeof(format_templ) - (size_t)(len + 1)),  "%d", fwidth);
                            HDstrncat(format_templ, "s", (sizeof(format_templ) - (size_t)(len + 1)));
                            fprintf(stream, format_templ, "UNDEF");
                        }
                    }
                    break;

                case 'c':
                    {
                        char x = (char)va_arg(ap, int);
                        n = fprintf(stream, format_templ, x);
                    }
                    break;

                case 's':
                case 'p':
                    {
                        char *x = va_arg(ap, char*); /*lint !e64 Type mismatch not really occuring */
                        n = fprintf(stream, format_templ, x);
                    }
                    break;

                case 'n':
                    format_templ[HDstrlen(format_templ) - 1] = 'u';
                    n = fprintf(stream, format_templ, nout);
                    break;

                case 't':
                    {
                        htri_t tri_var = va_arg(ap, htri_t);

                        if(tri_var > 0)
                            fprintf (stream, "TRUE");
                        else if(!tri_var)
                            fprintf(stream, "FALSE");
                        else
                            fprintf(stream, "FAIL(%d)", (int)tri_var);
                    }
                    break;

                default:
                    HDfputs(format_templ, stream);
                    n = (int)HDstrlen(format_templ);
                    break;
            }
            nout += n;
            fmt = s;
        } else {
            HDputc(*fmt, stream);
            fmt++;
            nout++;
        }
    }
    va_end(ap);
    return nout;
} /* end HDfprintf() */


/*-------------------------------------------------------------------------
 * Function:  HDstrtoll
 *
 * Purpose:  Converts the string S to an int64_t value according to the
 *    given BASE, which must be between 2 and 36 inclusive, or be
 *    the special value zero.
 *
 *    The string must begin with an arbitrary amount of white space
 *    (as determined by isspace(3c)) followed by a single optional
 *              `+' or `-' sign.  If BASE is zero or 16 the string may then
 *    include a `0x' or `0X' prefix, and the number will be read in
 *    base 16; otherwise a zero BASE is taken as 10 (decimal)
 *    unless the next character is a `0', in which case it is taken
 *    as 8 (octal).
 *
 *    The remainder of the string is converted to an int64_t in the
 *    obvious manner, stopping at the first character which is not
 *    a valid digit in the given base.  (In bases above 10, the
 *    letter `A' in either upper or lower case represetns 10, `B'
 *    represents 11, and so forth, with `Z' representing 35.)
 *
 *    If REST is not null, the address of the first invalid
 *    character in S is stored in *REST.  If there were no digits
 *    at all, the original value of S is stored in *REST.  Thus, if
 *    *S is not `\0' but **REST is `\0' on return the entire string
 *    was valid.
 *
 * Return:  Success:  The result.
 *
 *    Failure:  If the input string does not contain any
 *        digits then zero is returned and REST points
 *        to the original value of S.  If an overflow
 *        or underflow occurs then the maximum or
 *        minimum possible value is returned and the
 *        global `errno' is set to ERANGE.  If BASE is
 *        incorrect then zero is returned.
 *
 * Programmer:  Robb Matzke
 *              Thursday, April  9, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int64_t
HDstrtoll(const char *s, const char **rest, int base)
{
    int64_t  sign=1, acc=0;
    hbool_t  overflow = FALSE;

    errno = 0;
    if (!s || (base && (base<2 || base>36))) {
        if (rest)
            *rest = s;
        return 0;
    }

    /* Skip white space */
    while (HDisspace (*s)) s++;

    /* Optional minus or plus sign */
    if ('+'==*s) {
        s++;
    } else if ('-'==*s) {
        sign = -1;
        s++;
    }

    /* Zero base prefix */
    if (0==base && '0'==*s && ('x'==s[1] || 'X'==s[1])) {
        base = 16;
        s += 2;
    } else if (0==base && '0'==*s) {
        base = 8;
        s++;
    } else if (0==base) {
        base = 10;
    }

    /* Digits */
    while ((base<=10 && *s>='0' && *s<'0'+base) ||
     (base>10 && ((*s>='0' && *s<='9') ||
      (*s>='a' && *s<'a'+base-10) ||
      (*s>='A' && *s<'A'+base-10)))) {
        if (!overflow) {
            int64_t digit = 0;

            if (*s>='0' && *s<='9')
                digit = *s - '0';
            else if (*s>='a' && *s<='z')
                digit = (*s-'a')+10;
            else
                digit = (*s-'A')+10;

            if (acc*base+digit < acc) {
                overflow = TRUE;
            } else {
                acc = acc*base + digit;
            }
        }
        s++;
    }

    /* Overflow */
    if (overflow) {
        if (sign>0) {
            acc = ((uint64_t)1<<(8*sizeof(int64_t)-1))-1;
        } else {
            acc = (int64_t)((uint64_t)1<<(8*sizeof(int64_t)-1));
        }
        errno = ERANGE;
    }

    /* Return values */
    acc *= sign;
    if (rest)
        *rest = s;
    return acc;
} /* end HDstrtoll() */


/*-------------------------------------------------------------------------
 * Function:  HDrand/HDsrand
 *
 * Purpose:  Wrapper function for rand.  If rand_r exists on this system,
 *     use it.
 *
 *     Wrapper function for srand.  If rand_r is available, it will keep
 *     track of the seed locally instead of using srand() which modifies
 *     global state and can break other programs.
 *
 * Return:  Success:  Random number from 0 to RAND_MAX
 *
 *    Failure:  Cannot fail.
 *
 * Programmer:  Leon Arber
 *              March 6, 2006.
 *
 *-------------------------------------------------------------------------
 */
#ifdef H5_HAVE_RAND_R

static unsigned int g_seed = 42;

int HDrand(void)
{
    return rand_r(&g_seed);
}

void HDsrand(unsigned int seed)
{
    g_seed = seed;
}
#endif /* H5_HAVE_RAND_R */



/*-------------------------------------------------------------------------
 * Function:    Pflock
 *
 * Purpose:     Wrapper function for POSIX systems where flock(2) is not
 *              available.
 *
 * Return:      Success:    0
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
/* NOTE: Compile this all the time on POSIX systems, even when flock(2) is
 *       present so that it's less likely to become dead code.
 */
#ifdef H5_HAVE_FCNTL
int
Pflock(int fd, int operation) {
    
    struct flock    flk;

    /* Set the lock type */
    if(operation & LOCK_UN)
        flk.l_type = F_UNLCK;
    else if(operation & LOCK_SH)
        flk.l_type = F_RDLCK;
    else
        flk.l_type = F_WRLCK;

    /* Set the other flock struct values */
    flk.l_whence = SEEK_SET;
    flk.l_start = 0;
    flk.l_len = 0;              /* to EOF */
    flk.l_pid = 0;              /* not used with set */

    /* Lock or unlock */
    if(HDfcntl(fd, F_SETLK, flk) < 0)
        return -1;

    return 0;

} /* end Pflock() */
#endif /* H5_HAVE_FCNTL */


/*-------------------------------------------------------------------------
 * Function:    Nflock
 *
 * Purpose:     Wrapper function for systems where no file locking is
 *              available.
 *
 * Return:      Failure:    -1 (always fails)
 *
 *-------------------------------------------------------------------------
 */
int H5_ATTR_CONST
Nflock(int H5_ATTR_UNUSED fd, int H5_ATTR_UNUSED operation) {
    /* just fail */
    return -1;
} /* end Nflock() */


/*-------------------------------------------------------------------------
 * Function:	H5_make_time
 *
 * Purpose:	Portability routine to abstract converting a 'tm' struct into
 *		a time_t value.
 *
 * Note:	This is a little problematic because mktime() operates on
 *		local times.  We convert to local time and then figure out the
 *		adjustment based on the local time zone and daylight savings
 *		setting.
 *
 * Return:	Success:  The value of timezone
 *		Failure:  -1
 *
 * Programmer:  Quincey Koziol
 *              November 18, 2015
 *
 *-------------------------------------------------------------------------
 */
time_t
H5_make_time(struct tm *tm)
{
    time_t the_time;    /* The converted time */
#if defined(H5_HAVE_VISUAL_STUDIO) && (_MSC_VER >= 1900)  /* VS 2015 */
    /* In gcc and in Visual Studio prior to VS 2015 'timezone' is a global
     * variable declared in time.h. That variable was deprecated and in
     * VS 2015 is removed, with _get_timezone replacing it.
     */
    long timezone = 0;
#endif /* defined(H5_HAVE_VISUAL_STUDIO) && (_MSC_VER >= 1900) */
    time_t ret_value;   /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /* Sanity check */
    HDassert(tm);

    /* Initialize timezone information */
    if(!H5_ntzset) {
        HDtzset();
        H5_ntzset = TRUE;
    } /* end if */

    /* Perform base conversion */
    if((time_t)-1 == (the_time = HDmktime(tm)))
        HGOTO_ERROR(H5E_INTERNAL, H5E_CANTCONVERT, FAIL, "badly formatted modification time message")

    /* Adjust for timezones */
#if defined(H5_HAVE_TM_GMTOFF)
    /* BSD-like systems */
    the_time += tm->tm_gmtoff;
#elif defined(H5_HAVE_TIMEZONE)
#if defined(H5_HAVE_VISUAL_STUDIO) && (_MSC_VER >= 1900)  /* VS 2015 */
    /* In gcc and in Visual Studio prior to VS 2015 'timezone' is a global
     * variable declared in time.h. That variable was deprecated and in
     * VS 2015 is removed, with _get_timezone replacing it.
     */
    _get_timezone(&timezone);
#endif /* defined(H5_HAVE_VISUAL_STUDIO) && (_MSC_VER >= 1900) */

    the_time -= timezone - (tm->tm_isdst ? 3600 : 0);
#else
    /*
     * The catch-all.  If we can't convert a character string universal
     * coordinated time to a time_t value reliably then we can't decode the
     * modification time message. This really isn't as bad as it sounds -- the
     * only way a user can get the modification time is from our internal
     * query routines, which can gracefully recover.
     */
    HGOTO_ERROR(H5E_INTERNAL, H5E_UNSUPPORTED, FAIL, "unable to obtain local timezone information")
#endif

    /* Set return value */
    ret_value = the_time;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5_make_time() */

#ifdef H5_HAVE_WIN32_API

/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosecond units */
#define _W32_FT_OFFSET (116444736000000000ULL)


/*-------------------------------------------------------------------------
 * Function:  Wgettimeofday
 *
 * Purpose:  Wrapper function for gettimeofday on Windows systems
 *
 *     This function can get the time as well as a timezone
 *
 * Return:  0
 *
 *      This implementation is taken from the Cygwin source distribution at
 *          src/winsup/mingw/mingwex/gettimeofday.c
 *
 *      The original source code was contributed by
 *          Danny Smith <dannysmith@users.sourceforge.net>
 *      and released in the public domain.
 *
 * Programmer:  Scott Wegner
 *              May 19, 2009
 *
 *-------------------------------------------------------------------------
 */
int
Wgettimeofday(struct timeval *tv, struct timezone *tz)
{
  union {
    unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */
    FILETIME ft;
  }  _now;

    static int tzsetflag;

    if(tv) {
      GetSystemTimeAsFileTime (&_now.ft);
      tv->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL );
      tv->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL);
    }

    if(tz) {
        if(!tzsetflag) {
            _tzset();
            tzsetflag = 1;
        }
        tz->tz_minuteswest = _timezone / 60;
        tz->tz_dsttime = _daylight;
    }

  /* Always return 0 as per Open Group Base Specifications Issue 6.
     Do not set errno on error.  */
  return 0;
} /* end Wgettimeofday() */


/*-------------------------------------------------------------------------
 * Function:    Wsetenv
 *
 * Purpose:     Wrapper function for setenv on Windows systems.
 *              Interestingly, getenv *is* available in the Windows
 *              POSIX layer, just not setenv.
 *
 * Return:      Success:    0
 *              Failure:    non-zero error code
 *
 * Programmer:  Dana Robinson
 *              February 2016
 *
 *-------------------------------------------------------------------------
 */
int
Wsetenv(const char *name, const char *value, int overwrite)
{
    size_t bufsize;
    errno_t err;

    /* If we're not overwriting, check if the environment variable exists.
     * If it does (i.e.: the required buffer size to store the variable's
     * value is non-zero), then return an error code.
     */
    if(!overwrite) {
        err = getenv_s(&bufsize, NULL, 0, name);
        if (err || bufsize)
            return (int)err;
    } /* end if */

    return (int)_putenv_s(name, value);
} /* end Wsetenv() */

#ifdef H5_HAVE_WINSOCK2_H
#pragma comment(lib, "advapi32.lib")
#endif

#define WloginBuffer_count 256
static char Wlogin_buffer[WloginBuffer_count];

char*
Wgetlogin()
{

#ifdef H5_HAVE_WINSOCK2_H
    long bufferCount = WloginBuffer_count;
    if (GetUserName(Wlogin_buffer, &bufferCount) == 0)
        return (Wlogin_buffer);
    else
#endif /* H5_HAVE_WINSOCK2_H */
        return NULL;
}

int c99_snprintf(char* str, size_t size, const char* format, ...)
{
    int count;
    va_list ap;

    va_start(ap, format);
    count = c99_vsnprintf(str, size, format, ap);
    va_end(ap);

    return count;
}

int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)
{
    int count = -1;

    if (size != 0)
        count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
    if (count == -1)
        count = _vscprintf(format, ap);

    return count;
}


/*-------------------------------------------------------------------------
 * Function:    Wflock
 *
 * Purpose:     Wrapper function for flock on Windows systems
 *
 * Return:      Success:    0
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
int
Wflock(int H5_ATTR_UNUSED fd, int H5_ATTR_UNUSED operation) {

/* This is a no-op while we implement a Win32 VFD */
#if 0
int
Wflock(int fd, int operation) {

    HANDLE          hFile;
    DWORD           dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
    DWORD           dwReserved = 0;
                    /* MAXDWORD for entire file */
    DWORD           nNumberOfBytesToLockLow = MAXDWORD;
    DWORD           nNumberOfBytesToLockHigh = MAXDWORD;
                    /* Must initialize OVERLAPPED struct */
    OVERLAPPED      overlapped = {0};

    /* Get Windows HANDLE */
    hFile = _get_osfhandle(fd);

    /* Convert to Windows flags */
    if(operation & LOCK_EX)
        dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;

    /* Lock or unlock */
    if(operation & LOCK_UN)
        if(0 == UnlockFileEx(hFile, dwReserved, nNumberOfBytesToLockLow,
                            nNumberOfBytesToLockHigh, &overlapped))
            return -1;
    else
        if(0 == LockFileEx(hFile, dwFlags, dwReserved, nNumberOfBytesToLockLow,
                            nNumberOfBytesToLockHigh, &overlapped))
            return -1;
#endif /* 0 */
    return 0;
} /* end Wflock() */

#endif /* H5_HAVE_WIN32_API */


/*-------------------------------------------------------------------------
 * Function:    H5_build_extpath
 *
 * Purpose:     To build the path for later searching of target file for external
 *              links and external files.  This path can be either:
 *                  1. The absolute path of NAME
 *                      or
 *                  2. The current working directory + relative path of NAME
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Vailin Choi
 *              April 2, 2008
 *
 *-------------------------------------------------------------------------
 */
#define MAX_PATH_LEN     1024

herr_t
H5_build_extpath(const char *name, char **extpath /*out*/)
{
    char        *full_path = NULL;      /* Pointer to the full path, as built or passed in */
    char        *cwdpath = NULL;        /* Pointer to the current working directory path */
    char        *new_name = NULL;       /* Pointer to the name of the file */
    herr_t      ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /* Sanity check */
    HDassert(name);
    HDassert(extpath);

    /* Clear external path pointer to begin with */
    *extpath = NULL;

    /*
     * Unix: name[0] is a "/"
     * Windows: name[0-2] is "<drive letter>:\" or "<drive-letter>:/"
     */
    if(H5_CHECK_ABSOLUTE(name)) {
        if(NULL == (full_path = (char *)H5MM_strdup(name)))
            HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed")
    } /* end if */
    else { /* relative pathname */
        char *retcwd;
        size_t name_len;
        int drive;

        if(NULL == (cwdpath = (char *)H5MM_malloc(MAX_PATH_LEN)))
            HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed")
        name_len = HDstrlen(name) + 1;
        if(NULL == (new_name = (char *)H5MM_malloc(name_len)))
            HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed")

        /*
         * Windows: name[0-1] is "<drive-letter>:"
         *   Get current working directory on the drive specified in NAME
         * Unix: does not apply
         */
        if(H5_CHECK_ABS_DRIVE(name)) {
            drive = name[0] - 'A' + 1;
            retcwd = HDgetdcwd(drive, cwdpath, MAX_PATH_LEN);
            HDstrncpy(new_name, &name[2], name_len);
        } /* end if */
       /*
        * Windows: name[0] is a '/' or '\'
        *  Get current drive
        * Unix: does not apply
        */
        else if(H5_CHECK_ABS_PATH(name) && (0 != (drive = HDgetdrive()))) {
            HDsnprintf(cwdpath, MAX_PATH_LEN, "%c:%c", (drive + 'A' - 1), name[0]);
            retcwd = cwdpath;
            HDstrncpy(new_name, &name[1], name_len);
        }
        /* totally relative for Unix and Windows: get current working directory  */
        else {
            retcwd = HDgetcwd(cwdpath, MAX_PATH_LEN);
            HDstrncpy(new_name, name, name_len);
        } /* end if */

        if(retcwd != NULL) {
            size_t cwdlen;
            size_t path_len;

            HDassert(cwdpath);
            cwdlen = HDstrlen(cwdpath);
            HDassert(cwdlen);
            HDassert(new_name);
            path_len = cwdlen + HDstrlen(new_name) + 2;
            if(NULL == (full_path = (char *)H5MM_malloc(path_len)))
                HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed")

            HDstrncpy(full_path, cwdpath, cwdlen + 1);
            if(!H5_CHECK_DELIMITER(cwdpath[cwdlen - 1]))
                HDstrncat(full_path, H5_DIR_SEPS, HDstrlen(H5_DIR_SEPS));
            HDstrncat(full_path, new_name, HDstrlen(new_name));
        } /* end if */
    } /* end else */

    /* strip out the last component (the file name itself) from the path */
    if(full_path) {
        char *ptr = NULL;

        H5_GET_LAST_DELIMITER(full_path, ptr)
        HDassert(ptr);
        *++ptr = '\0';
        *extpath = full_path;
    } /* end if */

done:
    /* Release resources */
    if(cwdpath)
        H5MM_xfree(cwdpath);
    if(new_name)
        H5MM_xfree(new_name);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5_build_extpath() */


/*--------------------------------------------------------------------------
 * Function:    H5_combine_path
 *
 * Purpose:     If path2 is relative, interpret path2 as relative to path1
 *              and store the result in full_name. Otherwise store path2
 *              in full_name.
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  Steffen Kiess
 *              June 22, 2015
 *--------------------------------------------------------------------------
 */
herr_t
H5_combine_path(const char* path1, const char* path2, char **full_name /*out*/)
{
    size_t      path1_len;            /* length of path1 */
    size_t      path2_len;            /* length of path2 */
    herr_t      ret_value = SUCCEED;  /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    HDassert(path1);
    HDassert(path2);

    path1_len = HDstrlen(path1);
    path2_len = HDstrlen(path2);

    if(*path1 == '\0' || H5_CHECK_ABSOLUTE(path2)) {

        /* If path1 is empty or path2 is absolute, simply use path2 */
        if(NULL == (*full_name = (char *)H5MM_strdup(path2)))
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed")

    } /* end if */ 
    else if(H5_CHECK_ABS_PATH(path2)) {

        /* On windows path2 is a path absolute name */
        if (H5_CHECK_ABSOLUTE(path1) || H5_CHECK_ABS_DRIVE(path1)) {
            /* path1 is absolute or drive absolute and path2 is path absolute.
             * Use the drive letter of path1 + path2
             */
            if(NULL == (*full_name = (char *)H5MM_malloc(path2_len + 3)))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate path2 buffer")
            HDsnprintf(*full_name, (path2_len + 3), "%c:%s", path1[0], path2);
        } /* end if */
        else {
            /* On windows path2 is path absolute name ("\foo\bar"),
             * path1 does not have a drive letter (i.e. is "a\b" or "\a\b").
             * Use path2.
             */
            if(NULL == (*full_name = (char *)H5MM_strdup(path2)))
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed")
        } /* end else */

    } /* end else if */
    else {

        /* Relative path2:
         * Allocate a buffer to hold path1 + path2 + possibly the delimiter
         *      + terminating null byte
         */
        if(NULL == (*full_name = (char *)H5MM_malloc(path1_len + path2_len + 2)))
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate filename buffer")

        /* Compose the full file name */
        HDsnprintf(*full_name, (path1_len + path2_len + 2), "%s%s%s", path1,
                   (H5_CHECK_DELIMITER(path1[path1_len - 1]) ? "" : H5_DIR_SEPS), path2);
    } /* end else */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5_combine_name() */

