diff options
Diffstat (limited to 'src/H5system.c')
| -rw-r--r-- | src/H5system.c | 1782 |
1 files changed, 1189 insertions, 593 deletions
diff --git a/src/H5system.c b/src/H5system.c index bb1d2f1..d6d734f 100644 --- a/src/H5system.c +++ b/src/H5system.c @@ -1,25 +1,22 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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. * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://www.hdfgroup.org/licenses. * + * 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> + * Created: H5system.c + * Aug 21 2006 + * Quincey Koziol * - * Purpose: System call wrapper implementations. + * Purpose: System call wrapper implementations. * *------------------------------------------------------------------------- */ @@ -27,491 +24,77 @@ /****************/ /* Module Setup */ /****************/ - +#include "H5module.h" /* This source code file is part of the H5 module */ /***********/ /* Headers */ /***********/ -#include "H5private.h" /* Generic Functions */ -#include "H5Fprivate.h" /* File access */ -#include "H5MMprivate.h" /* Memory management */ -#include "H5Eprivate.h" - - +#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 */ /*******************/ - -/*------------------------------------------------------------------------- - * 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. - *------------------------------------------------------------------------- +/* Track whether tzset routine was called */ +static hbool_t H5_ntzset = FALSE; + +#ifndef H5_HAVE_VASPRINTF +/* HDvasprintf provides vasprintf-like function on targets where it is + * unavailable. */ int -HDfprintf(FILE *stream, const char *fmt, ...) +HDvasprintf(char **bufp, const char *fmt, va_list _ap) { - 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); + char *buf; /* buffer to receive formatted string */ + size_t bufsz; /* size of buffer to allocate */ + + for (bufsz = 32; (buf = HDmalloc(bufsz)) != NULL;) { + int ret; + va_list ap; + + HDva_copy(ap, _ap); + ret = HDvsnprintf(buf, bufsz, fmt, ap); + va_end(ap); + if (ret >= 0 && (size_t)ret < bufsz) { + *bufp = buf; + return ret; } - 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; + HDfree(buf); + if (ret < 0) + return ret; + bufsz = (size_t)ret + 1; } + return -1; +} +#endif /* H5_HAVE_VASPRINTF */ - /* Return values */ - acc *= sign; - if (rest) *rest = s; - return acc; -} /* end HDstrtoll() */ - - /*------------------------------------------------------------------------- * Function: HDrand/HDsrand * @@ -535,54 +118,164 @@ HDstrtoll(const char *s, const char **rest, int base) static unsigned int g_seed = 42; -int HDrand(void) +int +HDrand(void) { return rand_r(&g_seed); } -void HDsrand(unsigned int seed) +void +HDsrand(unsigned int seed) { g_seed = seed; } -#endif +#endif /* H5_HAVE_RAND_R */ - /*------------------------------------------------------------------------- - * Function: HDremove_all + * Function: Pflock * - * Purpose: Wrapper function for remove on VMS systems + * Purpose: Wrapper function for POSIX systems where flock(2) is not + * available. * - * This function deletes all versions of a file + * Return: Success: 0 + * Failure: -1 * - * Return: Success: 0; + *------------------------------------------------------------------------- + */ +/* 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 * - * Failure: -1 + * Purpose: Wrapper function for systems where no file locking is + * available. * - * Programmer: Elena Pourmal - * March 22, 2006 + * Return: 0 (success) * *------------------------------------------------------------------------- */ -#ifdef H5_VMS -int -HDremove_all(const char *fname) +int H5_ATTR_CONST +Nflock(int H5_ATTR_UNUSED fd, int H5_ATTR_UNUSED operation) { - 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; -} + /* just succeed */ + return 0; +} /* 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 = 0; /* 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 * @@ -604,191 +297,1094 @@ HDremove_all(const char *fname) * *------------------------------------------------------------------------- */ -#ifdef H5_HAVE_VISUAL_STUDIO - -/* 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; +{ + 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 (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) { + if (tz) { + if (!tzsetflag) { _tzset(); tzsetflag = 1; } tz->tz_minuteswest = _timezone / 60; - tz->tz_dsttime = _daylight; + tz->tz_dsttime = _daylight; } - /* Always return 0 as per Open Group Base Specifications Issue 6. - Do not set errno on error. */ - return 0; -} + /* Always return 0 as per Open Group Base Specifications Issue 6. + Do not set errno on error. */ + return 0; +} /* end Wgettimeofday() */ -#ifdef H5_HAVE_WINSOCK_H +/*------------------------------------------------------------------------- + * Function: Wsetenv + * + * Purpose: Wrapper function for setenv on Windows systems. + * Interestingly, getenv *is* available in the Windows + * POSIX layer, just not setenv. + * + * Note: Passing an empty string ("") for the value will remove + * the variable from the environment (like unsetenv(3)) + * + * Return: Success: 0 + * Failure: non-zero error code + * + * Programmer: Dana Robinson + * February 2016 + * + *------------------------------------------------------------------------- + */ +int +Wsetenv(const char *name, const char *value, int overwrite) +{ + /* 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) { + size_t bufsize; + errno_t err; + + 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_WIN32_API #pragma comment(lib, "advapi32.lib") #endif -#define WloginBuffer_count 256 -static char Wlogin_buffer[WloginBuffer_count]; - -char* -Wgetlogin() +/*------------------------------------------------------------------------- + * Function: H5_get_win32_times + * + * Purpose: Gets the elapsed, system and user times on Windows platforms. + * All time values are in seconds. + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Dana Robinson + * May 2011 + * + *------------------------------------------------------------------------- + */ +#ifdef H5_HAVE_WIN32_API +int +H5_get_win32_times(H5_timevals_t *tvs /*in,out*/) { + static HANDLE process_handle; + ULARGE_INTEGER kernel_start; + ULARGE_INTEGER user_start; + FILETIME KernelTime; + FILETIME UserTime; + FILETIME CreationTime; + FILETIME ExitTime; + LARGE_INTEGER counts_start; + static LARGE_INTEGER counts_freq; + static hbool_t is_initialized = FALSE; + BOOL err; + + HDassert(tvs); + + if (!is_initialized) { + /* NOTE: This is just a pseudo handle and does not need to be closed. */ + process_handle = GetCurrentProcess(); + err = QueryPerformanceFrequency(&counts_freq); + if (0 == err) + return -1; + is_initialized = TRUE; + } /* end if */ -#ifdef H5_HAVE_WINSOCK_H - long bufferCount = WloginBuffer_count; - if (GetUserName(Wlogin_buffer, &bufferCount) == 0) - return (Wlogin_buffer); - else -#endif /* H5_HAVE_WINSOCK_H */ - return NULL; -} + /************************* + * System and user times * + *************************/ + + err = GetProcessTimes(process_handle, &CreationTime, &ExitTime, &KernelTime, &UserTime); + if (0 == err) + return -1; + + /* The 1.0E7 factor seems strange but it's due to the clock + * ticking in 100 ns increments. + */ + kernel_start.HighPart = KernelTime.dwHighDateTime; + kernel_start.LowPart = KernelTime.dwLowDateTime; + tvs->system = (double)(kernel_start.QuadPart / 1.0E7F); + + user_start.HighPart = UserTime.dwHighDateTime; + user_start.LowPart = UserTime.dwLowDateTime; + tvs->user = (double)(user_start.QuadPart / 1.0E7F); + + /**************** + * Elapsed time * + ****************/ + + err = QueryPerformanceCounter(&counts_start); + if (0 == err) + return -1; + tvs->elapsed = (double)(counts_start.QuadPart) / (double)counts_freq.QuadPart; + + return 0; +} /* end H5_get_win32_times() */ #endif - /*------------------------------------------------------------------------- - * Function: H5_build_extpath + * Function: Wflock + * + * Purpose: Wrapper function for flock on Windows systems * - * Purpose: To build the path for later searching of target file for external - * link. This path can be either: + * Return: Success: 0 + * Failure: -1 + * + *------------------------------------------------------------------------- + */ +int +Wflock(int fd, int operation) +{ + + HANDLE hFile; + DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; + DWORD dwReserved = 0; + /* MAXDWORD locks the entire file */ + DWORD nNumberOfBytesToLockLow = MAXDWORD; + DWORD nNumberOfBytesToLockHigh = MAXDWORD; + /* Must initialize OVERLAPPED struct */ + OVERLAPPED overlapped = {0}; + + /* Get Windows HANDLE */ + if (INVALID_HANDLE_VALUE == (hFile = (HANDLE)_get_osfhandle(fd))) + return -1; + + /* 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)) { + /* Attempting to unlock an already unlocked file will fail and this can happen + * in H5Fstart_swmr_write(). For now, just ignore the "error" (error code: 0x9e / 158). + */ + if (GetLastError() != 158) + return -1; + } + } + else { + if (0 == LockFileEx(hFile, dwFlags, dwReserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh, + &overlapped)) + return -1; + } + + return 0; +} /* end Wflock() */ + +/*------------------------------------------------------------------------- + * Function: H5_get_utf16_str + * + * Purpose: Gets a UTF-16 string from an UTF-8 (or ASCII) string. + * + * Return: Success: A pointer to a UTF-16 string + * This must be freed by the caller using H5MM_xfree() + * Failure: NULL + * + * Programmer: Dana Robinson + * Spring 2019 + * + *------------------------------------------------------------------------- + */ +wchar_t * +H5_get_utf16_str(const char *s) +{ + int nwchars = -1; /* Length of the UTF-16 buffer */ + wchar_t *ret_s = NULL; /* UTF-16 version of the string */ + + /* Get the number of UTF-16 characters needed */ + if (0 == (nwchars = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0))) + goto error; + + /* Allocate a buffer for the UTF-16 string */ + if (NULL == (ret_s = (wchar_t *)H5MM_calloc(sizeof(wchar_t) * (size_t)nwchars))) + goto error; + + /* Convert the input UTF-8 string to UTF-16 */ + if (0 == MultiByteToWideChar(CP_UTF8, 0, s, -1, ret_s, nwchars)) + goto error; + + return ret_s; + +error: + if (ret_s) + H5MM_xfree((void *)ret_s); + return NULL; +} /* end H5_get_utf16_str() */ + +/*------------------------------------------------------------------------- + * Function: Wopen_utf8 + * + * Purpose: UTF-8 equivalent of open(2) for use on Windows. + * Converts a UTF-8 input path to UTF-16 and then opens the + * file via _wopen() under the hood + * + * Return: Success: A POSIX file descriptor + * Failure: -1 + * + * Programmer: Dana Robinson + * Spring 2019 + * + *------------------------------------------------------------------------- + */ +int +Wopen_utf8(const char *path, int oflag, ...) +{ + int fd = -1; /* POSIX file descriptor to be returned */ + wchar_t *wpath = NULL; /* UTF-16 version of the path */ + int pmode = 0; /* mode (optionally set via variable args) */ + + /* Convert the input UTF-8 path to UTF-16 */ + if (NULL == (wpath = H5_get_utf16_str(path))) + goto done; + + /* _O_BINARY must be set in Windows to avoid CR-LF <-> LF EOL + * transformations when performing I/O. Note that this will + * produce Unix-style text files, though. + */ + oflag |= _O_BINARY; + + /* Get the mode, if O_CREAT was specified */ + if (oflag & O_CREAT) { + va_list vl; + + HDva_start(vl, oflag); + pmode = HDva_arg(vl, int); + HDva_end(vl); + } + + /* Open the file */ + fd = _wopen(wpath, oflag, pmode); + +done: + if (wpath) + H5MM_xfree((void *)wpath); + + return fd; +} /* end Wopen_utf8() */ + +/*------------------------------------------------------------------------- + * Function: Wremove_utf8 + * + * Purpose: UTF-8 equivalent of remove(3) for use on Windows. + * Converts a UTF-8 input path to UTF-16 and then opens the + * file via _wremove() under the hood + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Dana Robinson + * Spring 2019 + * + *------------------------------------------------------------------------- + */ +int +Wremove_utf8(const char *path) +{ + wchar_t *wpath = NULL; /* UTF-16 version of the path */ + int ret = -1; + + /* Convert the input UTF-8 path to UTF-16 */ + if (NULL == (wpath = H5_get_utf16_str(path))) + goto done; + + /* Open the file */ + ret = _wremove(wpath); + +done: + if (wpath) + H5MM_xfree((void *)wpath); + + return ret; +} /* end Wremove_utf8() */ + +#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: Success: 0 - * Failure: -1 + * Return: SUCCEED/FAIL * * Programmer: Vailin Choi - * April 2, 2008 + * April 2, 2008 * *------------------------------------------------------------------------- */ -#define MAX_PATH_LEN 1024 +#define MAX_PATH_LEN 1024 herr_t -H5_build_extpath(const char *name, char **extpath/*out*/) +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 */ + 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>:/" - * 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))) + 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 */ + } /* end if */ else { /* relative pathname */ - char *retcwd; - int drive; + char *retcwd; + size_t name_len; + int drive; - if(NULL == (cwdpath = (char *)H5MM_malloc(MAX_PATH_LEN))) + 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))) + 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 - * OpenVMS: does not apply - */ - if(CHECK_ABS_DRIVE(name)) { - drive = name[0] - 'A' + 1; + /* + * 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 = HDtoupper(name[0]) - 'A' + 1; retcwd = HDgetdcwd(drive, cwdpath, MAX_PATH_LEN); - HDstrcpy(new_name, &name[2]); + HDstrncpy(new_name, &name[2], name_len); } /* 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]); + /* + * 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; - HDstrcpy(new_name, &name[1]); + HDstrncpy(new_name, &name[1], name_len); } - /* totally relative for Unix, Windows, and OpenVMS: get current working directory */ - else + /* 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) { + 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))) + 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 + HDstrncpy(full_path, cwdpath, cwdlen + 1); + if (!H5_CHECK_DELIMITER(cwdpath[cwdlen - 1])) + HDstrncat(full_path, H5_DIR_SEPS, path_len - (cwdlen + 1)); + HDstrncat(full_path, new_name, path_len - (cwdlen + 1) - HDstrlen(H5_DIR_SEPS)); } /* end if */ - } /* end else */ + } /* end else */ /* strip out the last component (the file name itself) from the path */ - if(full_path) { + if (full_path) { char *ptr = NULL; - GET_LAST_DELIMITER(full_path, ptr) + H5_GET_LAST_DELIMITER(full_path, ptr) HDassert(ptr); - *++ptr = '\0'; + *++ptr = '\0'; *extpath = full_path; } /* end if */ done: /* Release resources */ - if(cwdpath) - H5MM_xfree(cwdpath); - if(new_name) - H5MM_xfree(new_name); + 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 = 0; /* length of path1 */ + size_t path2_len; /* length of path2 */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + HDassert(path2); + + if (path1) + path1_len = HDstrlen(path1); + path2_len = HDstrlen(path2); + + if (path1 == NULL || *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 + + 2))) /* Extra "+2" to quiet GCC warning - 2019/07/05, QAK */ + 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 + 2), "%s%s%s", + path1, /* Extra "+2" to quiet GCC warning - 2019/07/05, QAK */ + (H5_CHECK_DELIMITER(path1[path1_len - 1]) ? "" : H5_DIR_SEPS), path2); + } /* end else */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5_combine_path() */ + +/*-------------------------------------------------------------------------- + * Function: H5_nanosleep + * + * Purpose: Sleep for a given # of nanoseconds + * + * Note that commodity hardware is probably going to have a + * resolution of milliseconds, not nanoseconds. + * + * Return: void + *-------------------------------------------------------------------------- + */ +void +H5_nanosleep(uint64_t nanosec) +{ + FUNC_ENTER_NOAPI_NOINIT_NOERR + +#ifdef H5_HAVE_WIN32_API + DWORD dwMilliseconds = (DWORD)HDceil(nanosec / 1.0e6); + DWORD ignore; + + /* Windows can't sleep at a ns resolution. Best we can do is ~1 ms. We + * don't care about the return value since the second parameter + * (bAlertable) is FALSE, so it will always be zero. + */ + ignore = SleepEx(dwMilliseconds, FALSE); + +#else + + const uint64_t nanosec_per_sec = 1000 * 1000L * 1000; + struct timespec sleeptime; /* Struct to hold time to sleep */ + + /* Set up time to sleep + * + * Assuming ILP32 or LP64 or wider architecture, (long)operand + * satisfies 0 <= operand < nanosec_per_sec < LONG_MAX. + * + * It's harder to be sure that we don't overflow time_t. + */ + sleeptime.tv_sec = (time_t)(nanosec / nanosec_per_sec); + sleeptime.tv_nsec = (long)(nanosec % nanosec_per_sec); + + /* Sleep for up to `sleeptime` and, in the event of an interruption, + * save the unslept time back to `sleeptime`. + */ + while (HDnanosleep(&sleeptime, &sleeptime) == -1) { + /* If we were just interrupted, sleep for the remaining time. + * Otherwise, the error was essentially impossible, so just stop + * sleeping. + */ + if (errno != EINTR) + break; + } +#endif + + FUNC_LEAVE_NOAPI_VOID +} /* end H5_nanosleep() */ + +#ifdef H5_HAVE_WIN32_API + +#define H5_WIN32_ENV_VAR_BUFFER_SIZE 32767 + +/*------------------------------------------------------------------------- + * Function: H5_expand_windows_env_vars() + * + * Purpose: Replaces windows environment variables of the form %foo% + * with user-specific values. + * + * Return: SUCCEED/FAIL + * + *------------------------------------------------------------------------- + */ +herr_t +H5_expand_windows_env_vars(char **env_var) +{ + long n_chars = 0; + char *temp_buf = NULL; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + /* Allocate buffer for expanded environment variable string */ + if (NULL == (temp_buf = (char *)H5MM_calloc((size_t)H5_WIN32_ENV_VAR_BUFFER_SIZE))) + HGOTO_ERROR(H5E_PLUGIN, H5E_CANTALLOC, FAIL, "can't allocate memory for expanded path") + + /* Expand the environment variable string */ + if ((n_chars = ExpandEnvironmentStringsA(*env_var, temp_buf, H5_WIN32_ENV_VAR_BUFFER_SIZE)) > + H5_WIN32_ENV_VAR_BUFFER_SIZE) + HGOTO_ERROR(H5E_PLUGIN, H5E_NOSPACE, FAIL, "expanded path is too long") + + if (0 == n_chars) + HGOTO_ERROR(H5E_PLUGIN, H5E_CANTGET, FAIL, "failed to expand path") + + *env_var = (char *)H5MM_xfree(*env_var); + *env_var = temp_buf; + +done: + if (FAIL == ret_value && temp_buf) + temp_buf = (char *)H5MM_xfree(temp_buf); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5_expand_windows_env_vars() */ + +/*------------------------------------------------------------------------- + * Function: H5_strndup + * + * Purpose: Similar to strndup() for use on Windows. Allocates a new + * string and copies at most `n` bytes from the original + * string into the new string. If the original string is + * longer than `n`, only `n` bytes are copied from the + * original string. In either case, the string being returned + * is guaranteed to be terminated with a null byte. + * + * The returned pointer is allocated by H5MM_malloc in this + * routine and must be freed by the caller with H5MM_free or + * H5MM_xfree. + * + * Return: Pointer to copied string on success + * NULL on failure + * + *------------------------------------------------------------------------- + */ +char * +H5_strndup(const char *s, size_t n) +{ + size_t len; + char *ret_value = NULL; + + FUNC_ENTER_NOAPI_NOINIT + + if (!s) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "string cannot be NULL") + + for (len = 0; len < n && s[len] != '\0'; len++) + ; + + if (NULL == (ret_value = H5MM_malloc(len + 1))) + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "can't allocate buffer for string") + + H5MM_memcpy(ret_value, s, len); + ret_value[len] = '\0'; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} +#endif /* H5_HAVE_WIN32_API */ + +/* dirname() and basename() are not easily ported to Windows and basename + * behavior varies depending on if you get POSIX vs. GNU. As a more + * platform-indpendent work-around, we've implemented H5_ versions of + * dirname() and basename(). + * + * - The input string is never modified. + * + * - The out parameter is a new string that was allocated with H5MM routines + * and must be freed by the caller via H5MM_free()/H5MM_xfree(). + * + * - NULL pointers are errors. + * + * - On errors, FAIL will be returned and the output parameter will be + * undefined. + * + * - Assumes the file separator is \ on Win32 and / everywhere else, + * including Cygwin. + */ + +/*------------------------------------------------------------------------- + * Function: H5_dirname + * + * Purpose: Similar to dirname(3) but more portable across platforms. + * Returns a pointer to the directory component of a specified + * pathname. The returned pointer is allocated by this routine + * and must be freed by the caller with H5MM_free or + * H5MM_xfree. + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5_dirname(const char *path, char **dirname) +{ + char *sep; + char *out = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + if (!path) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "path can't be NULL") + if (!dirname) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "dirname can't be NULL") + + if (NULL == (sep = HDstrrchr(path, H5_DIR_SEPC))) { + /* Pathname with no file separator characters */ + out = H5MM_strdup("."); + } + else if (sep == path) { + /* Pathname of form "/" or "/filename" */ + out = H5MM_strdup(H5_DIR_SEPS); + } + else { + if (sep[1] == '\0') { + /* + * Last file separator character is last character in + * pathname. Skip this and any other preceding trailing + * file separator characters + */ + while (sep != path && sep[-1] == H5_DIR_SEPC) + sep--; + + if (sep == path) { + /* Contrived case: "//", "///" and similar */ + out = H5MM_strdup(H5_DIR_SEPS); + sep = NULL; + } + else { + /* + * Must have found the filename component. Search + * backwards to a previous file separator character, + * if any. + */ + while (sep != path && sep[-1] != H5_DIR_SEPC) + sep--; + + if (sep == path) { + /* No directory component found, just return "." */ + out = H5MM_strdup("."); + sep = NULL; + } + } + } + + if (sep) { + ptrdiff_t len; + + /* Skip a possible run of duplicate file separator characters */ + while (sep != path && sep[-1] == H5_DIR_SEPC) + sep--; + + if (sep == path) + /* Pathname of form "/usr/" */ + out = H5MM_strdup(H5_DIR_SEPS); + else { + /* Pathname of form "dir/filename" */ + len = sep - path; + HDassert(len >= 0); + + out = H5MM_strndup(path, (size_t)len); + } + } + } + + if (NULL == out) + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate buffer for dirname"); + + *dirname = out; + +done: + if (FAIL == ret_value) { + H5MM_free(out); + if (dirname) + *dirname = NULL; + } FUNC_LEAVE_NOAPI(ret_value) -} /* H5_build_extpath() */ +} /* end H5_dirname() */ + +/*------------------------------------------------------------------------- + * Function: H5_basename + * + * Purpose: Similar to basename(3) but more portable across platforms. + * Returns a pointer to the filename component of a specified + * pathname. The returned pointer is allocated by this routine + * and must be freed by the caller with H5MM_free or + * H5MM_xfree. + * + * NOTE: This routine follows the POSIX semantics for + * basename(3). That is, passing the path string "/" ("\" on + * Windows) returns the string "/" (again, "\" on Windows) and + * passing a path string with trailing file separator + * characters returns the filename component with the trailing + * file separator characters being ignored. + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5_basename(const char *path, char **basename) +{ + const char *sep; + char *out = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + if (!path) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "path can't be NULL") + if (!basename) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "basename can't be NULL") + + if (NULL == (sep = HDstrrchr(path, H5_DIR_SEPC))) { + if (*path == '\0') + /* Empty pathname */ + out = H5MM_strdup("."); + else + /* Pathname with no file separator characters */ + out = H5MM_strdup(path); + } + else if (sep == path) { + if (sep[1] == '\0') + /* Pathname of form "/" */ + out = H5MM_strdup(H5_DIR_SEPS); + else + /* Pathname of form "/filename" */ + out = H5MM_strdup(sep + 1); + } + else { + if (sep[1] != '\0') + /* Pathname of form "dir/filename" */ + out = H5MM_strdup(sep + 1); + else { + /* Pathname of form "filename/", "/dir/filename/", etc. */ + + /* + * Last file separator character is last character in + * pathname. Skip this and any other preceding trailing + * file separator characters + */ + while (sep != path && sep[-1] == H5_DIR_SEPC) + sep--; + + if (sep == path) + /* Contrived case: "//", "///" and similar */ + out = H5MM_strdup(H5_DIR_SEPS); + else { + const char *c_ptr = sep; + ptrdiff_t len; + + /* + * Skip back to a previous file separator character, + * if any, and form final filename component + */ + while (c_ptr != path && c_ptr[-1] != H5_DIR_SEPC) + c_ptr--; + + len = sep - c_ptr; + HDassert(len >= 0); + + out = H5MM_strndup(c_ptr, (size_t)len); + } + } + } + + if (NULL == out) + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate buffer for basename"); + + *basename = out; +done: + if (FAIL == ret_value) { + H5MM_free(out); + if (basename) + *basename = NULL; + } + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5_basename() */ + +/* Global variables */ +int H5_opterr = 1; /* Get_option prints errors if this is on */ +int H5_optind = 1; /* Token pointer */ +const char *H5_optarg; /* Flag argument (or value) */ + +/*------------------------------------------------------------------------- + * Function: H5_get_option + * + * Purpose: Determine the command-line options a user specified. We can + * accept both short and long type command-lines. + * + * Return: Success: The short valued "name" of the command line + * parameter or EOF if there are no more + * parameters to process. + * + * Failure: A question mark. + *------------------------------------------------------------------------- + */ +int +H5_get_option(int argc, const char *const *argv, const char *opts, const struct h5_long_options *l_opts) +{ + static int sp = 1; /* character index in current token */ + int optchar = '?'; /* option character passed back to user */ + + if (sp == 1) { + /* check for more flag-like tokens */ + if (H5_optind >= argc || argv[H5_optind][0] != '-' || argv[H5_optind][1] == '\0') { + return EOF; + } + else if (HDstrcmp(argv[H5_optind], "--") == 0) { + H5_optind++; + return EOF; + } + } + + if (sp == 1 && argv[H5_optind][0] == '-' && argv[H5_optind][1] == '-') { + /* long command line option */ + int i; + const char ch = '='; + char *arg = HDstrdup(&argv[H5_optind][2]); + size_t arg_len = 0; + + H5_optarg = strchr(&argv[H5_optind][2], ch); + arg_len = HDstrlen(&argv[H5_optind][2]); + if (H5_optarg) { + arg_len -= HDstrlen(H5_optarg); + H5_optarg++; /* skip the equal sign */ + } + arg[arg_len] = 0; + + for (i = 0; l_opts && l_opts[i].name; i++) { + if (HDstrcmp(arg, l_opts[i].name) == 0) { + /* we've found a matching long command line flag */ + optchar = l_opts[i].shortval; + + if (l_opts[i].has_arg != no_arg) { + if (H5_optarg == NULL) { + if (l_opts[i].has_arg != optional_arg) { + if (H5_optind < (argc - 1)) + if (argv[H5_optind + 1][0] != '-') + H5_optarg = argv[++H5_optind]; + } + else if (l_opts[i].has_arg == require_arg) { + if (H5_opterr) + HDfprintf(stderr, "%s: option required for \"--%s\" flag\n", argv[0], arg); + + optchar = '?'; + } + } + } + else { + if (H5_optarg) { + if (H5_opterr) + HDfprintf(stderr, "%s: no option required for \"%s\" flag\n", argv[0], arg); + + optchar = '?'; + } + } + break; + } + } + + if (l_opts[i].name == NULL) { + /* exhausted all of the l_opts we have and still didn't match */ + if (H5_opterr) + HDfprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], arg); + + optchar = '?'; + } + + H5_optind++; + sp = 1; + + HDfree(arg); + } + else { + char *cp; /* pointer into current token */ + + /* short command line option */ + optchar = argv[H5_optind][sp]; + + if (optchar == ':' || (cp = HDstrchr(opts, optchar)) == 0) { + if (H5_opterr) + HDfprintf(stderr, "%s: unknown option \"%c\"\n", argv[0], optchar); + + /* if no chars left in this token, move to next token */ + if (argv[H5_optind][++sp] == '\0') { + H5_optind++; + sp = 1; + } + return '?'; + } + + if (*++cp == ':') { + /* if a value is expected, get it */ + if (argv[H5_optind][sp + 1] != '\0') { + /* flag value is rest of current token */ + H5_optarg = &argv[H5_optind++][sp + 1]; + } + else if (++H5_optind >= argc) { + if (H5_opterr) + HDfprintf(stderr, "%s: value expected for option \"%c\"\n", argv[0], optchar); + + optchar = '?'; + } + else { + /* flag value is next token */ + H5_optarg = argv[H5_optind++]; + } + + sp = 1; + } + /* wildcard argument */ + else if (*cp == '*') { + /* check the next argument */ + H5_optind++; + /* we do have an extra argument, check if not last */ + if ((H5_optind + 1) < argc) { + if (argv[H5_optind][0] != '-') { + H5_optarg = argv[H5_optind++]; + } + else { + H5_optarg = NULL; + } + } + else { + H5_optarg = NULL; + } + } + else { + /* set up to look at next char in token, next time */ + if (argv[H5_optind][++sp] == '\0') { + /* no more in current token, so setup next token */ + H5_optind++; + sp = 1; + } + H5_optarg = NULL; + } + } + + /* return the current flag character found */ + return optchar; +} + +/*------------------------------------------------------------------------- + * Function: H5_strcasestr + * + * Purpose: Find the first occurrence of the substring needle in the + * string haystack ignoring case. + * + * Return: Success: Pointer to the beginning of the located substring + * + * Failure: NULL + * + *------------------------------------------------------------------------- + */ +char * +H5_strcasestr(const char *haystack, const char *needle) +{ + /* Check arguments. */ + HDassert(haystack); + HDassert(needle); + + /* begin once from each character of haystack, until needle is found */ + do { + const char *h = haystack; + const char *n = needle; + /* loop while lowercase strings match, or needle ends */ + while (HDtolower(*h) == HDtolower(*n) && *n) { + h++; + n++; + } + /* if all characters in needle matched we found it */ + if (*n == 0) { + /* must discard const qualifier here, so turn off the warning */ + H5_GCC_CLANG_DIAG_OFF("cast-qual") + return (char *)haystack; + H5_GCC_CLANG_DIAG_ON("cast-qual") + } + } while (*haystack++); + return 0; +} /* end H5_strcasestr() */ |
