/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 "H5Fprivate.h"		/* File access				*/
#include "H5MMprivate.h"	/* Memory management			*/
#include "H5Eprivate.h"



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


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


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


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


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


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


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


/*-------------------------------------------------------------------------
 * 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];
    const char	*s;
    va_list	ap;

    assert (stream);
    assert (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;
		} /* 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;
	    } 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 ("ZHhlqLI", *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)) {
			HDstrcpy (modifier, "l");
		    } else {
			HDstrcpy (modifier, H5_PRINTF_LL_WIDTH);
		    }
		    break;
		case 'Z':
		    if (sizeof(size_t)<sizeof(long)) {
			modifier[0] = '\0';
		    } else if (sizeof(size_t)==sizeof(long)) {
			HDstrcpy (modifier, "l");
		    } else {
			HDstrcpy (modifier, H5_PRINTF_LL_WIDTH);
		    }
		    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 */
	    sprintf (format_templ, "%%%s%s%s%s%s",
		     leftjust?"-":"", plussign?"+":"",
		     ldspace?" ":"", prefix?"#":"", zerofill?"0":"");
	    if (fwidth>0)
		sprintf (format_templ+HDstrlen(format_templ), "%d", fwidth);
	    if (prec>0)
		sprintf (format_templ+HDstrlen(format_templ), ".%d", prec);
	    if (*modifier)
		sprintf (format_templ+HDstrlen(format_templ), "%s", modifier);
	    sprintf (format_templ+HDstrlen(format_templ), "%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, 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)) {
			sprintf(format_templ, "%%%s%s%s%s%s",
				leftjust?"-":"", plussign?"+":"",
				ldspace?" ":"", prefix?"#":"",
				zerofill?"0":"");
			if (fwidth>0)
			    sprintf(format_templ+HDstrlen(format_templ), "%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) {
			    HDstrcat(format_templ, "u");
			} else if (sizeof(x)==H5_SIZEOF_LONG) {
			    HDstrcat(format_templ, "lu");
			} else if (sizeof(x)==H5_SIZEOF_LONG_LONG) {
			    HDstrcat(format_templ, H5_PRINTF_LL_WIDTH);
			    HDstrcat(format_templ, "u");
			}
			n = fprintf(stream, format_templ, x);
		    } else {
			HDstrcpy(format_templ, "%");
			if (leftjust)
                            HDstrcat(format_templ, "-");
			if (fwidth)
			    sprintf(format_templ+HDstrlen(format_templ), "%d", fwidth);
			HDstrcat(format_templ, "s");
			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


/*-------------------------------------------------------------------------
 * Function:	HDremove_all
 *
 * Purpose:	Wrapper function for remove on VMS systems
 *
 * 		This function deletes all versions of a file
 *
 * Return:	Success:        0;
 *
 *		Failure:	-1
 *
 * Programmer:	Elena Pourmal
 *              March 22, 2006
 *
 *-------------------------------------------------------------------------
 */
#ifdef H5_VMS
int
HDremove_all(const char *fname)
{
    int ret_value = -1;
    char *_fname;

    _fname = (char *)H5MM_malloc(HDstrlen(fname) + 3); /* to accomodate ;* and null */
    if(_fname) {
        HDstrcpy(_fname, fname);
        HDstrcat(_fname,";*");
        /* Do not use HDremove; function becomes recursive (see H5private.h file)*/
        remove(_fname);
        H5MM_xfree(_fname);
        ret_value = 0;
    }
    return ret_value;
}
#endif

/*-------------------------------------------------------------------------
 * 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
 *
 *-------------------------------------------------------------------------
 */
#ifdef _WIN32

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

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;
}
#endif


/*-------------------------------------------------------------------------
 * Function: H5_build_extpath
 *
 * Purpose:  To build the path for later searching of target file for external
 *		link.  This path can be either:
 *                  1. The absolute path of NAME
 *                      or
 *                  2. The current working directory + relative path of NAME
 *
 * Return:	Success:        0
 *		Failure:	-1
 *
 * 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(H5_build_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>:/"
     * OpenVMS: <disk name>$<partition>:[path]<file name>
     *     i.g. SYS$SYSUSERS:[LU.HDF5.SRC]H5system.c
     */
    if(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;
        int drive;

        if(NULL == (cwdpath = (char *)H5MM_malloc(MAX_PATH_LEN)))
            HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed")
        if(NULL == (new_name = (char *)H5MM_strdup(name)))
            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
         * OpenVMS: does not apply
	 */
        if(CHECK_ABS_DRIVE(name)) {
            drive = name[0] - 'A' + 1;
            retcwd = HDgetdcwd(drive, cwdpath, MAX_PATH_LEN);
            HDstrcpy(new_name, &name[2]);
        } /* end if */
	/*
	 * Windows: name[0] is a '/' or '\'
	 *	Get current drive
	 * Unix: does not apply
         * OpenVMS: does not apply
	 */
        else if(CHECK_ABS_PATH(name) && (0 != (drive = HDgetdrive()))) {
            sprintf(cwdpath, "%c:%c", (drive+'A'-1), name[0]);
            retcwd = cwdpath;
            HDstrcpy(new_name, &name[1]);
        }
        /* totally relative for Unix, Windows, and OpenVMS: get current working directory  */
        else
            retcwd = HDgetcwd(cwdpath, MAX_PATH_LEN);

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

            cwdlen = HDstrlen(cwdpath);
            HDassert(cwdlen);
            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")

            HDstrcpy(full_path, cwdpath);
#ifdef H5_VMS
            /* If the file name contains relative path, cut off the beginning bracket.  Also cut off the
             * ending bracket of CWDPATH to combine the full path name. i.g.
             *     cwdpath = SYS$SYSUSERS:[LU.HDF5.TEST]
             *     new_name = [.tmp]extlinks.h5
             *     full_path = SYS$SYSUSERS:[LU.HDF5.TEST.tmp]extlinks.h5
             */
            if(new_name[0] == '[') {
                char *tmp = new_name;
                full_path[cwdlen - 1] = '\0';
                HDstrcat(full_path, ++tmp);
            } /* end if */
            else
                HDstrcat(full_path, new_name);
#else
            if(!CHECK_DELIMITER(cwdpath[cwdlen - 1]))
                HDstrcat(full_path, DIR_SEPS);
            HDstrcat(full_path, new_name);
#endif
        } /* end if */
    } /* end else */

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

        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)
} /* H5_build_extpath() */

