Coverage Report

Created: 2025-11-03 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5system.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*-------------------------------------------------------------------------
14
 *
15
 * Created:     H5system.c
16
 *
17
 * Purpose:     System call wrapper implementations.
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
22
/****************/
23
/* Module Setup */
24
/****************/
25
#include "H5module.h" /* This source code file is part of the H5 module */
26
27
/***********/
28
/* Headers */
29
/***********/
30
#include "H5private.h"   /* Generic Functions        */
31
#include "H5Eprivate.h"  /* Error handling           */
32
#include "H5Fprivate.h"  /* File access              */
33
#include "H5MMprivate.h" /* Memory management        */
34
35
/****************/
36
/* Local Macros */
37
/****************/
38
39
/******************/
40
/* Local Typedefs */
41
/******************/
42
43
/********************/
44
/* Package Typedefs */
45
/********************/
46
47
/********************/
48
/* Local Prototypes */
49
/********************/
50
51
/*********************/
52
/* Package Variables */
53
/*********************/
54
55
/*****************************/
56
/* Library Private Variables */
57
/*****************************/
58
59
/*******************/
60
/* Local Variables */
61
/*******************/
62
63
/* Track whether tzset routine was called */
64
static bool H5_ntzset = false;
65
66
#ifndef H5_HAVE_VASPRINTF
67
/* HDvasprintf provides vasprintf-like function on targets where it is
68
 * unavailable.
69
 */
70
int
71
HDvasprintf(char **bufp, const char *fmt, va_list _ap)
72
{
73
    char  *buf;   /* buffer to receive formatted string */
74
    size_t bufsz; /* size of buffer to allocate */
75
76
    for (bufsz = 32; (buf = malloc(bufsz)) != NULL;) {
77
        int     ret;
78
        va_list ap;
79
80
        va_copy(ap, _ap);
81
        ret = vsnprintf(buf, bufsz, fmt, ap);
82
        va_end(ap);
83
        if (ret >= 0 && (size_t)ret < bufsz) {
84
            *bufp = buf;
85
            return ret;
86
        }
87
        free(buf);
88
        if (ret < 0)
89
            return ret;
90
        bufsz = (size_t)ret + 1;
91
    }
92
    return -1;
93
}
94
#endif /* H5_HAVE_VASPRINTF */
95
96
/*-------------------------------------------------------------------------
97
 * Function:    Pflock
98
 *
99
 * Purpose:     Wrapper function for POSIX systems where flock(2) is not
100
 *              available.
101
 *
102
 * Return:      Success:    0
103
 *              Failure:    -1
104
 *
105
 *-------------------------------------------------------------------------
106
 */
107
/* NOTE: Compile this all the time on POSIX systems, even when flock(2) is
108
 *       present so that it's less likely to become dead code.
109
 */
110
#ifdef H5_HAVE_FCNTL
111
int
112
Pflock(int fd, int operation)
113
0
{
114
115
0
    struct flock flk;
116
117
    /* Set the lock type */
118
0
    if (operation & LOCK_UN)
119
0
        flk.l_type = F_UNLCK;
120
0
    else if (operation & LOCK_SH)
121
0
        flk.l_type = F_RDLCK;
122
0
    else
123
0
        flk.l_type = F_WRLCK;
124
125
    /* Set the other flock struct values */
126
0
    flk.l_whence = SEEK_SET;
127
0
    flk.l_start  = 0;
128
0
    flk.l_len    = 0; /* to EOF */
129
0
    flk.l_pid    = 0; /* not used with set */
130
131
    /* Lock or unlock */
132
0
    if (fcntl(fd, F_SETLK, &flk) < 0)
133
0
        return -1;
134
135
0
    return 0;
136
137
0
} /* end Pflock() */
138
#endif /* H5_HAVE_FCNTL */
139
140
/*-------------------------------------------------------------------------
141
 * Function:    Nflock
142
 *
143
 * Purpose:     Wrapper function for systems where no file locking is
144
 *              available.
145
 *
146
 * Return:      0 (success)
147
 *
148
 *-------------------------------------------------------------------------
149
 */
150
int H5_ATTR_CONST
151
Nflock(int H5_ATTR_UNUSED fd, int H5_ATTR_UNUSED operation)
152
0
{
153
    /* just succeed */
154
0
    return 0;
155
0
} /* end Nflock() */
156
157
/*-------------------------------------------------------------------------
158
 * Function:    H5_make_time
159
 *
160
 * Purpose:    Portability routine to abstract converting a 'tm' struct into
161
 *        a time_t value.
162
 *
163
 * Note:    This is a little problematic because mktime() operates on
164
 *        local times.  We convert to local time and then figure out the
165
 *        adjustment based on the local time zone and daylight savings
166
 *        setting.
167
 *
168
 * Return:    Success:  The value of timezone
169
 *        Failure:  -1
170
 *
171
 *-------------------------------------------------------------------------
172
 */
173
time_t
174
H5_make_time(struct tm *tm)
175
0
{
176
0
    time_t the_time; /* The converted time */
177
#if defined(H5_HAVE_VISUAL_STUDIO)
178
    long timezone = 0;
179
#endif
180
0
    time_t ret_value = 0; /* Return value */
181
182
0
    FUNC_ENTER_NOAPI_NOINIT
183
184
    /* Sanity check */
185
0
    assert(tm);
186
187
    /* Initialize timezone information */
188
0
    if (!H5_ntzset) {
189
0
        tzset();
190
0
        H5_ntzset = true;
191
0
    }
192
193
    /* Perform base conversion */
194
0
    if ((time_t)-1 == (the_time = mktime(tm)))
195
0
        HGOTO_ERROR(H5E_INTERNAL, H5E_CANTCONVERT, FAIL, "badly formatted modification time message");
196
197
        /* Adjust for timezones */
198
0
#if defined(H5_HAVE_TM_GMTOFF)
199
    /* BSD-like systems */
200
0
    the_time += tm->tm_gmtoff;
201
#elif defined(H5_HAVE_TIMEZONE)
202
#if defined(H5_HAVE_VISUAL_STUDIO)
203
    _get_timezone(&timezone);
204
#endif
205
206
    the_time -= timezone - (tm->tm_isdst ? 3600 : 0);
207
#else
208
    /*
209
     * The catch-all.  If we can't convert a character string universal
210
     * coordinated time to a time_t value reliably then we can't decode the
211
     * modification time message. This really isn't as bad as it sounds -- the
212
     * only way a user can get the modification time is from our internal
213
     * query routines, which can gracefully recover.
214
     */
215
    HGOTO_ERROR(H5E_INTERNAL, H5E_UNSUPPORTED, FAIL, "unable to obtain local timezone information");
216
#endif
217
218
    /* Set return value */
219
0
    ret_value = the_time;
220
221
0
done:
222
0
    FUNC_LEAVE_NOAPI(ret_value)
223
0
} /* end H5_make_time() */
224
225
#ifdef H5_HAVE_WIN32_API
226
227
/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosecond units */
228
#define _W32_FT_OFFSET (116444736000000000ULL)
229
230
/*-------------------------------------------------------------------------
231
 * Function:  Wgettimeofday
232
 *
233
 * Purpose:  Wrapper function for gettimeofday on Windows systems
234
 *
235
 *     This function can get the time as well as a timezone
236
 *
237
 * Return:  0
238
 *
239
 *      This implementation is taken from the Cygwin source distribution at
240
 *          src/winsup/mingw/mingwex/gettimeofday.c
241
 *
242
 *      The original source code was contributed by
243
 *          Danny Smith <dannysmith@users.sourceforge.net>
244
 *      and released in the public domain.
245
 *
246
 *-------------------------------------------------------------------------
247
 */
248
int
249
Wgettimeofday(struct timeval *tv, struct timezone *tz)
250
{
251
    union {
252
        unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */
253
        FILETIME           ft;
254
    } _now;
255
256
    static int tzsetflag;
257
258
    if (tv) {
259
        GetSystemTimeAsFileTime(&_now.ft);
260
        tv->tv_usec = (long)((_now.ns100 / 10ULL) % 1000000ULL);
261
        tv->tv_sec  = (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL);
262
    }
263
264
    if (tz) {
265
        if (!tzsetflag) {
266
            _tzset();
267
            tzsetflag = 1;
268
        }
269
        tz->tz_minuteswest = _timezone / 60;
270
        tz->tz_dsttime     = _daylight;
271
    }
272
273
    /* Always return 0 as per Open Group Base Specifications Issue 6.
274
       Do not set errno on error.  */
275
    return 0;
276
} /* end Wgettimeofday() */
277
278
/*-------------------------------------------------------------------------
279
 * Function:    Wsetenv
280
 *
281
 * Purpose:     Wrapper function for setenv on Windows systems.
282
 *              Interestingly, getenv *is* available in the Windows
283
 *              POSIX layer, just not setenv.
284
 *
285
 * Note:        Passing an empty string ("") for the value will remove
286
 *              the variable from the environment (like unsetenv(3))
287
 *
288
 * Return:      Success:    0
289
 *              Failure:    non-zero error code
290
 *
291
 *-------------------------------------------------------------------------
292
 */
293
int
294
Wsetenv(const char *name, const char *value, int overwrite)
295
{
296
    /* If we're not overwriting, check if the environment variable exists.
297
     * If it does (i.e.: the required buffer size to store the variable's
298
     * value is non-zero), then return an error code.
299
     */
300
    if (!overwrite) {
301
#ifndef H5_HAVE_MINGW
302
        size_t  bufsize;
303
        errno_t err;
304
305
        err = getenv_s(&bufsize, NULL, 0, name);
306
        if (err || bufsize)
307
            return (int)err;
308
#else
309
        /* MinGW doesn't have getenv_s() */
310
        char *test = getenv(name);
311
        if (*test)
312
            return FAIL;
313
#endif
314
    }
315
316
    return (int)_putenv_s(name, value);
317
} /* end Wsetenv() */
318
319
#ifdef H5_HAVE_WIN32_API
320
#pragma comment(lib, "advapi32.lib")
321
#endif
322
323
/*-------------------------------------------------------------------------
324
 * Function:    H5_get_win32_times
325
 *
326
 * Purpose:     Gets the elapsed, system and user times on Windows platforms.
327
 *              All time values are in seconds.
328
 *
329
 * Return:      Success:  0
330
 *              Failure:  -1
331
 *
332
 *-------------------------------------------------------------------------
333
 */
334
#ifdef H5_HAVE_WIN32_API
335
int
336
H5_get_win32_times(H5_timevals_t *tvs /*in,out*/)
337
{
338
    static HANDLE        process_handle;
339
    ULARGE_INTEGER       kernel_start;
340
    ULARGE_INTEGER       user_start;
341
    FILETIME             KernelTime;
342
    FILETIME             UserTime;
343
    FILETIME             CreationTime;
344
    FILETIME             ExitTime;
345
    LARGE_INTEGER        counts_start;
346
    static LARGE_INTEGER counts_freq;
347
    static bool          is_initialized = false;
348
    BOOL                 err;
349
350
    assert(tvs);
351
352
    if (!is_initialized) {
353
        /* NOTE: This is just a pseudo handle and does not need to be closed. */
354
        process_handle = GetCurrentProcess();
355
        err            = QueryPerformanceFrequency(&counts_freq);
356
        if (0 == err)
357
            return -1;
358
        is_initialized = true;
359
    } /* end if */
360
361
    /*************************
362
     * System and user times *
363
     *************************/
364
365
    err = GetProcessTimes(process_handle, &CreationTime, &ExitTime, &KernelTime, &UserTime);
366
    if (0 == err)
367
        return -1;
368
369
    /* The 1.0E7 factor seems strange but it's due to the clock
370
     * ticking in 100 ns increments.
371
     */
372
    kernel_start.HighPart = KernelTime.dwHighDateTime;
373
    kernel_start.LowPart  = KernelTime.dwLowDateTime;
374
    tvs->system           = (double)(kernel_start.QuadPart / 1.0E7F);
375
376
    user_start.HighPart = UserTime.dwHighDateTime;
377
    user_start.LowPart  = UserTime.dwLowDateTime;
378
    tvs->user           = (double)(user_start.QuadPart / 1.0E7F);
379
380
    /****************
381
     * Elapsed time *
382
     ****************/
383
384
    err = QueryPerformanceCounter(&counts_start);
385
    if (0 == err)
386
        return -1;
387
388
    tvs->elapsed = (double)(counts_start.QuadPart) / (double)counts_freq.QuadPart;
389
390
    return 0;
391
} /* end H5_get_win32_times() */
392
#endif
393
394
/*-------------------------------------------------------------------------
395
 * Function:    Wflock
396
 *
397
 * Purpose:     Wrapper function for flock on Windows systems
398
 *
399
 * Return:      Success:    0
400
 *              Failure:    -1
401
 *
402
 *-------------------------------------------------------------------------
403
 */
404
int
405
Wflock(int fd, int operation)
406
{
407
408
    HANDLE hFile;
409
    DWORD  dwFlags    = LOCKFILE_FAIL_IMMEDIATELY;
410
    DWORD  dwReserved = 0;
411
    /* MAXDWORD locks the entire file */
412
    DWORD nNumberOfBytesToLockLow  = MAXDWORD;
413
    DWORD nNumberOfBytesToLockHigh = MAXDWORD;
414
    /* Must initialize OVERLAPPED struct */
415
    OVERLAPPED overlapped = {0};
416
417
    /* Get Windows HANDLE */
418
    if (INVALID_HANDLE_VALUE == (hFile = (HANDLE)_get_osfhandle(fd)))
419
        return -1;
420
421
    /* Convert to Windows flags */
422
    if (operation & LOCK_EX)
423
        dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
424
425
    /* Lock or unlock */
426
    if (operation & LOCK_UN) {
427
        if (0 ==
428
            UnlockFileEx(hFile, dwReserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh, &overlapped)) {
429
            /* Attempting to unlock an already unlocked file will fail and this can happen
430
             * in H5Fstart_swmr_write(). For now, just ignore the "error" (error code: 0x9e / 158).
431
             */
432
            if (GetLastError() != 158)
433
                return -1;
434
        }
435
    }
436
    else {
437
        if (0 == LockFileEx(hFile, dwFlags, dwReserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh,
438
                            &overlapped))
439
            return -1;
440
    }
441
442
    return 0;
443
} /* end Wflock() */
444
445
/*-------------------------------------------------------------------------
446
 * Function:     H5_get_utf16_str
447
 *
448
 * Purpose:      Gets a UTF-16 string from an UTF-8 (or ASCII) string.
449
 *
450
 *               On success, a pointer to the new UTF-16 string is returned
451
 *               in `wstring`. This must be freed by the caller using
452
 *               H5MM_xfree().
453
 *
454
 * Return:       Non-negative on success/Negative on failure
455
 *
456
 *               On failure, the result of GetLastError() is returned
457
 *               through the `win_error` parameter, if non-NULL.
458
 *
459
 *-------------------------------------------------------------------------
460
 */
461
herr_t
462
H5_get_utf16_str(const char *s, wchar_t **wstring, uint32_t *win_error)
463
{
464
    int      nwchars = -1;   /* Length of the UTF-16 buffer */
465
    wchar_t *ret_s   = NULL; /* UTF-16 version of the string */
466
467
    /* Get the number of UTF-16 characters needed */
468
    if (0 == (nwchars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, NULL, 0)))
469
        goto error;
470
471
    /* Allocate a buffer for the UTF-16 string */
472
    if (NULL == (ret_s = H5MM_calloc(sizeof(wchar_t) * (size_t)nwchars)))
473
        goto error;
474
475
    /* Convert the input UTF-8 string to UTF-16 */
476
    if (0 == MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, ret_s, nwchars))
477
        goto error;
478
479
    *wstring = ret_s;
480
481
    return SUCCEED;
482
483
error:
484
    /* Store error value first before doing anything else */
485
    if (win_error)
486
        *win_error = (uint32_t)GetLastError();
487
488
    if (ret_s)
489
        H5MM_xfree((void *)ret_s);
490
491
    *wstring = NULL;
492
493
    return FAIL;
494
} /* end H5_get_utf16_str() */
495
496
/*-------------------------------------------------------------------------
497
 * Function:     Wopen
498
 *
499
 * Purpose:      Equivalent of open(2) for use on Windows. Necessary to
500
 *               handle Unicode and code pages on that platform.
501
 *
502
 * Return:       Success:    A POSIX file descriptor
503
 *               Failure:    -1
504
 *-------------------------------------------------------------------------
505
 */
506
int
507
Wopen(const char *path, int oflag, ...)
508
{
509
    uint32_t win_error        = 0;     /* Windows error code for failures */
510
    wchar_t *wpath            = NULL;  /* UTF-16 version of the path */
511
    herr_t   h5_ret           = FAIL;  /* HDF5 return code */
512
    char    *env              = NULL;  /* Environment variable string */
513
    bool     prefer_code_page = false; /* Whether to prefer using the Windows code page */
514
    int      fd               = -1;    /* POSIX file descriptor to be returned */
515
    int      pmode            = 0;     /* mode (optionally set via variable args) */
516
517
    /* _O_BINARY must be set in Windows to avoid CR-LF <-> LF EOL
518
     * transformations when performing I/O. Note that this will
519
     * produce Unix-style text files, though.
520
     */
521
    oflag |= _O_BINARY;
522
523
    /* Get the mode, if O_CREAT was specified */
524
    if (oflag & O_CREAT) {
525
        va_list vl;
526
527
        va_start(vl, oflag);
528
        pmode = va_arg(vl, int);
529
        va_end(vl);
530
    }
531
532
    /*
533
     * Check HDF5_PREFER_WINDOWS_CODE_PAGE environment variable to
534
     * determine how to handle the pathname.
535
     */
536
    env = getenv(HDF5_PREFER_WINDOWS_CODE_PAGE);
537
    if (env && (*env != '\0')) {
538
        if (0 == HDstrcasecmp(env, "true") || 0 == strcmp(env, "1"))
539
            prefer_code_page = true;
540
    }
541
542
    /*
543
     * Unless requested to prefer Windows code pages, try to convert
544
     * the pathname from UTF-8 to UTF-16. If this fails, fallback to
545
     * the normal POSIX open() call.
546
     */
547
    if (!prefer_code_page) {
548
        h5_ret = H5_get_utf16_str(path, &wpath, &win_error);
549
        if (h5_ret >= 0) {
550
            /* Open the file using a UTF-16 path */
551
            fd = _wopen(wpath, oflag, pmode);
552
        }
553
        else {
554
            if (ERROR_NO_UNICODE_TRANSLATION != win_error)
555
                goto done;
556
557
            fd = open(path, oflag, pmode);
558
        }
559
    }
560
    else
561
        fd = open(path, oflag, pmode);
562
563
done:
564
    H5MM_xfree(wpath);
565
566
    return fd;
567
} /* end Wopen() */
568
569
/*-------------------------------------------------------------------------
570
 * Function:     Wremove
571
 *
572
 * Purpose:      Equivalent of remove(3) for use on Windows. Necessary to
573
 *               handle Unicode and code pages on that platform.
574
 *
575
 * Return:       Success:    0
576
 *               Failure:    -1
577
 *-------------------------------------------------------------------------
578
 */
579
int
580
Wremove(const char *path)
581
{
582
    uint32_t win_error        = 0;     /* Windows error code for failures */
583
    wchar_t *wpath            = NULL;  /* UTF-16 version of the path */
584
    herr_t   h5_ret           = FAIL;  /* HDF5 return code */
585
    char    *env              = NULL;  /* Environment variable string */
586
    bool     prefer_code_page = false; /* Whether to prefer using the Windows code page */
587
    int      ret              = -1;
588
589
    /*
590
     * Check HDF5_PREFER_WINDOWS_CODE_PAGE environment variable to
591
     * determine how to handle the pathname.
592
     */
593
    env = getenv(HDF5_PREFER_WINDOWS_CODE_PAGE);
594
    if (env && (*env != '\0')) {
595
        if (0 == HDstrcasecmp(env, "true") || 0 == strcmp(env, "1"))
596
            prefer_code_page = true;
597
    }
598
599
    /*
600
     * Unless requested to prefer Windows code pages, try to convert
601
     * the pathname from UTF-8 to UTF-16. If this fails, fallback to
602
     * the normal POSIX remove() call.
603
     */
604
    if (!prefer_code_page) {
605
        h5_ret = H5_get_utf16_str(path, &wpath, &win_error);
606
        if (h5_ret >= 0) {
607
            /* Remove the file using a UTF-16 path */
608
            ret = _wremove(wpath);
609
        }
610
        else {
611
            if (ERROR_NO_UNICODE_TRANSLATION != win_error)
612
                goto done;
613
614
            ret = remove(path);
615
        }
616
    }
617
    else
618
        ret = remove(path);
619
620
done:
621
    H5MM_xfree(wpath);
622
623
    return ret;
624
} /* end Wremove() */
625
626
#endif /* H5_HAVE_WIN32_API */
627
628
/*-------------------------------------------------------------------------
629
 * Function:    H5_build_extpath
630
 *
631
 * Purpose:     To build the path for later searching of target file for external
632
 *              links and external files.  This path can be either:
633
 *                  1. The absolute path of NAME
634
 *                      or
635
 *                  2. The current working directory + relative path of NAME
636
 *
637
 * Return:      SUCCEED/FAIL
638
 *
639
 *-------------------------------------------------------------------------
640
 */
641
0
#define MAX_PATH_LEN 1024
642
643
herr_t
644
H5_build_extpath(const char *name, char **extpath /*out*/)
645
0
{
646
0
    char  *full_path = NULL;    /* Pointer to the full path, as built or passed in */
647
0
    char  *cwdpath   = NULL;    /* Pointer to the current working directory path */
648
0
    char  *new_name  = NULL;    /* Pointer to the name of the file */
649
0
    herr_t ret_value = SUCCEED; /* Return value */
650
651
0
    FUNC_ENTER_NOAPI_NOINIT
652
653
0
    assert(name);
654
0
    assert(extpath);
655
656
    /* Clear external path pointer to begin with */
657
0
    *extpath = NULL;
658
659
    /*
660
     * Unix: name[0] is a "/"
661
     * Windows: name[0-2] is "<drive letter>:\" or "<drive-letter>:/"
662
     */
663
0
    if (H5_CHECK_ABSOLUTE(name)) {
664
0
        if (NULL == (full_path = (char *)H5MM_strdup(name)))
665
0
            HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed");
666
0
    }
667
0
    else {
668
        /* relative pathname */
669
0
        char  *retcwd;
670
0
        size_t name_len;
671
0
        int    drive;
672
673
0
        if (NULL == (cwdpath = (char *)H5MM_malloc(MAX_PATH_LEN)))
674
0
            HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed");
675
0
        name_len = strlen(name) + 1;
676
0
        if (NULL == (new_name = (char *)H5MM_malloc(name_len)))
677
0
            HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed");
678
679
        /*
680
         * Windows: name[0-1] is "<drive-letter>:"
681
         *   Get current working directory on the drive specified in NAME
682
         * Unix: does not apply
683
         */
684
0
        if (H5_CHECK_ABS_DRIVE(name)) {
685
0
            drive  = toupper(name[0]) - 'A' + 1;
686
0
            retcwd = HDgetdcwd(drive, cwdpath, MAX_PATH_LEN);
687
0
            strncpy(new_name, &name[2], name_len);
688
0
        }
689
        /*
690
         * Windows: name[0] is a '/' or '\'
691
         *  Get current drive
692
         * Unix: does not apply
693
         */
694
0
        else if (H5_CHECK_ABS_PATH(name) && (0 != (drive = HDgetdrive()))) {
695
0
            snprintf(cwdpath, MAX_PATH_LEN, "%c:%c", (drive + 'A' - 1), name[0]);
696
0
            retcwd = cwdpath;
697
0
            strncpy(new_name, &name[1], name_len);
698
0
        }
699
        /* totally relative for Unix and Windows: get current working directory  */
700
0
        else {
701
0
            retcwd = HDgetcwd(cwdpath, MAX_PATH_LEN);
702
0
            strncpy(new_name, name, name_len);
703
0
        }
704
705
0
        if (retcwd != NULL) {
706
0
            size_t cwdlen;
707
0
            size_t path_len;
708
709
0
            cwdlen = strlen(cwdpath);
710
0
            if (cwdlen == 0)
711
0
                HGOTO_ERROR(H5E_INTERNAL, H5E_BADVALUE, FAIL, "cwd length is zero");
712
0
            path_len = cwdlen + strlen(new_name) + 2;
713
0
            if (NULL == (full_path = (char *)H5MM_malloc(path_len)))
714
0
                HGOTO_ERROR(H5E_INTERNAL, H5E_NOSPACE, FAIL, "memory allocation failed");
715
716
            /* path_len will always be greater than zero, so no check before
717
             * setting the terminal NUL byte of full_path
718
             */
719
0
            strncpy(full_path, cwdpath, path_len);
720
0
            full_path[path_len - 1] = '\0';
721
722
0
            if (!H5_CHECK_DELIMITER(cwdpath[cwdlen - 1]))
723
0
                strncat(full_path, H5_DIR_SEPS, path_len - (cwdlen + 1));
724
0
            strncat(full_path, new_name, path_len - (cwdlen + 1) - strlen(H5_DIR_SEPS));
725
0
        }
726
0
    }
727
728
    /* Strip out the last component (the file name itself) from the path */
729
0
    if (full_path) {
730
0
        char *ptr = NULL;
731
732
0
        H5_GET_LAST_DELIMITER(full_path, ptr)
733
0
        assert(ptr);
734
0
        *++ptr   = '\0';
735
0
        *extpath = full_path;
736
0
    }
737
738
0
done:
739
0
    if (cwdpath)
740
0
        H5MM_xfree(cwdpath);
741
0
    if (new_name)
742
0
        H5MM_xfree(new_name);
743
744
0
    FUNC_LEAVE_NOAPI(ret_value)
745
0
} /* end H5_build_extpath() */
746
747
/*--------------------------------------------------------------------------
748
 * Function:    H5_combine_path
749
 *
750
 * Purpose:     If path2 is relative, interpret path2 as relative to path1
751
 *              and store the result in full_name. Otherwise store path2
752
 *              in full_name.
753
 *
754
 * Return:      SUCCEED/FAIL
755
 *
756
 *--------------------------------------------------------------------------
757
 */
758
herr_t
759
H5_combine_path(const char *path1, const char *path2, char **full_name /*out*/)
760
0
{
761
0
    size_t path1_len = 0;       /* length of path1 */
762
0
    size_t path2_len;           /* length of path2 */
763
0
    herr_t ret_value = SUCCEED; /* Return value */
764
765
0
    FUNC_ENTER_NOAPI_NOINIT
766
767
0
    assert(path2);
768
769
0
    if (path1)
770
0
        path1_len = strlen(path1);
771
0
    path2_len = strlen(path2);
772
773
0
    if (path1 == NULL || *path1 == '\0' || H5_CHECK_ABSOLUTE(path2)) {
774
775
        /* If path1 is empty or path2 is absolute, simply use path2 */
776
0
        if (NULL == (*full_name = (char *)H5MM_strdup(path2)))
777
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
778
779
0
    } /* end if */
780
0
    else if (H5_CHECK_ABS_PATH(path2)) {
781
782
        /* On windows path2 is a path absolute name */
783
0
        if (H5_CHECK_ABSOLUTE(path1) || H5_CHECK_ABS_DRIVE(path1)) {
784
            /* path1 is absolute or drive absolute and path2 is path absolute.
785
             * Use the drive letter of path1 + path2
786
             */
787
0
            if (NULL == (*full_name = (char *)H5MM_malloc(path2_len + 3)))
788
0
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate path2 buffer");
789
0
            snprintf(*full_name, (path2_len + 3), "%c:%s", path1[0], path2);
790
0
        } /* end if */
791
0
        else {
792
            /* On windows path2 is path absolute name ("\foo\bar"),
793
             * path1 does not have a drive letter (i.e. is "a\b" or "\a\b").
794
             * Use path2.
795
             */
796
0
            if (NULL == (*full_name = (char *)H5MM_strdup(path2)))
797
0
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
798
0
        } /* end else */
799
800
0
    } /* end else if */
801
0
    else {
802
803
        /* Relative path2:
804
         * Allocate a buffer to hold path1 + path2 + possibly the delimiter
805
         *      + terminating null byte
806
         */
807
0
        if (NULL ==
808
0
            (*full_name = (char *)H5MM_malloc(path1_len + path2_len + 2 +
809
0
                                              2))) /* Extra "+2" to quiet GCC warning - 2019/07/05, QAK */
810
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate filename buffer");
811
812
        /* Compose the full file name */
813
0
        snprintf(*full_name, (path1_len + path2_len + 2 + 2), "%s%s%s",
814
0
                 path1, /* Extra "+2" to quiet GCC warning - 2019/07/05, QAK */
815
0
                 (H5_CHECK_DELIMITER(path1[path1_len - 1]) ? "" : H5_DIR_SEPS), path2);
816
0
    } /* end else */
817
818
0
done:
819
0
    FUNC_LEAVE_NOAPI(ret_value)
820
0
} /* end H5_combine_path() */
821
822
/*--------------------------------------------------------------------------
823
 * Function:    H5_nanosleep
824
 *
825
 * Purpose:     Sleep for a given # of nanoseconds
826
 *
827
 *              Note that commodity hardware is probably going to have a
828
 *              resolution of milliseconds, not nanoseconds.
829
 *
830
 * Return:      void
831
 *--------------------------------------------------------------------------
832
 */
833
void
834
H5_nanosleep(uint64_t nanosec)
835
0
{
836
0
    FUNC_ENTER_NOAPI_NOINIT_NOERR
837
838
#ifdef H5_HAVE_WIN32_API
839
    DWORD dwMilliseconds = (DWORD)ceil(nanosec / 1.0e6);
840
841
    /* Windows can't sleep at a ns resolution. Best we can do is ~1 ms. We
842
     * don't care about the return value since the second parameter
843
     * (bAlertable) is false, so it will always be zero.
844
     */
845
    SleepEx(dwMilliseconds, false);
846
847
#else
848
849
0
    const uint64_t  nanosec_per_sec = 1000 * 1000L * 1000;
850
0
    struct timespec sleeptime; /* Struct to hold time to sleep */
851
852
    /* Set up time to sleep
853
     *
854
     * Assuming ILP32 or LP64 or wider architecture, (long)operand
855
     * satisfies 0 <= operand < nanosec_per_sec < LONG_MAX.
856
     *
857
     * It's harder to be sure that we don't overflow time_t.
858
     */
859
0
    sleeptime.tv_sec  = (time_t)(nanosec / nanosec_per_sec);
860
0
    sleeptime.tv_nsec = (long)(nanosec % nanosec_per_sec);
861
862
    /* Sleep for up to `sleeptime` and, in the event of an interruption,
863
     * save the unslept time back to `sleeptime`.
864
     */
865
0
    while (HDnanosleep(&sleeptime, &sleeptime) == -1) {
866
        /* If we were just interrupted, sleep for the remaining time.
867
         * Otherwise, the error was essentially impossible, so just stop
868
         * sleeping.
869
         */
870
0
        if (errno != EINTR)
871
0
            break;
872
0
    }
873
0
#endif
874
875
0
    FUNC_LEAVE_NOAPI_VOID
876
0
} /* end H5_nanosleep() */
877
878
#ifdef H5_HAVE_WIN32_API
879
880
#define H5_WIN32_ENV_VAR_BUFFER_SIZE 32767
881
882
/*-------------------------------------------------------------------------
883
 * Function:    H5_expand_windows_env_vars()
884
 *
885
 * Purpose:     Replaces windows environment variables of the form %foo%
886
 *              with user-specific values.
887
 *
888
 * Return:      SUCCEED/FAIL
889
 *
890
 *-------------------------------------------------------------------------
891
 */
892
herr_t
893
H5_expand_windows_env_vars(char **env_var)
894
{
895
    long   n_chars   = 0;
896
    char  *temp_buf  = NULL;
897
    herr_t ret_value = SUCCEED; /* Return value */
898
899
    FUNC_ENTER_NOAPI_NOINIT
900
901
    /* Allocate buffer for expanded environment variable string */
902
    if (NULL == (temp_buf = (char *)H5MM_calloc((size_t)H5_WIN32_ENV_VAR_BUFFER_SIZE)))
903
        HGOTO_ERROR(H5E_PLUGIN, H5E_CANTALLOC, FAIL, "can't allocate memory for expanded path");
904
905
    /* Expand the environment variable string */
906
    if ((n_chars = ExpandEnvironmentStringsA(*env_var, temp_buf, H5_WIN32_ENV_VAR_BUFFER_SIZE)) >
907
        H5_WIN32_ENV_VAR_BUFFER_SIZE)
908
        HGOTO_ERROR(H5E_PLUGIN, H5E_NOSPACE, FAIL, "expanded path is too long");
909
910
    if (0 == n_chars)
911
        HGOTO_ERROR(H5E_PLUGIN, H5E_CANTGET, FAIL, "failed to expand path");
912
913
    *env_var = (char *)H5MM_xfree(*env_var);
914
    *env_var = temp_buf;
915
916
done:
917
    if (FAIL == ret_value && temp_buf)
918
        temp_buf = (char *)H5MM_xfree(temp_buf);
919
920
    FUNC_LEAVE_NOAPI(ret_value)
921
} /* end H5_expand_windows_env_vars() */
922
923
/*-------------------------------------------------------------------------
924
 * Function:    H5_strndup
925
 *
926
 * Purpose:     Similar to strndup() for use on Windows. Allocates a new
927
 *              string and copies at most `n` bytes from the original
928
 *              string into the new string. If the original string is
929
 *              longer than `n`, only `n` bytes are copied from the
930
 *              original string. In either case, the string being returned
931
 *              is guaranteed to be terminated with a null byte.
932
 *
933
 *              The returned pointer is allocated by H5MM_malloc in this
934
 *              routine and must be freed by the caller with H5MM_free or
935
 *              H5MM_xfree.
936
 *
937
 * Return:      Pointer to copied string on success
938
 *              NULL on failure
939
 *
940
 *-------------------------------------------------------------------------
941
 */
942
char *
943
H5_strndup(const char *s, size_t n)
944
{
945
    size_t len;
946
    char  *ret_value = NULL;
947
948
    FUNC_ENTER_NOAPI_NOINIT
949
950
    if (!s)
951
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "string cannot be NULL");
952
953
    for (len = 0; len < n && s[len] != '\0'; len++)
954
        ;
955
956
    if (NULL == (ret_value = H5MM_malloc(len + 1)))
957
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "can't allocate buffer for string");
958
959
    H5MM_memcpy(ret_value, s, len);
960
    ret_value[len] = '\0';
961
962
done:
963
    FUNC_LEAVE_NOAPI(ret_value)
964
}
965
966
/*-------------------------------------------------------------------------
967
 * Function:    Wstrcasestr_wrap
968
 *
969
 * Purpose:     Windows wrapper function for strcasestr to retain GNU
970
 *              behavior where searching for an empty substring returns the
971
 *              input string being searched. StrStrIA on Windows does not
972
 *              exhibit this same behavior.
973
 *
974
 * Return:      Pointer to input string if 'needle' is the empty substring
975
 *              Otherwise, returns StrStrIA(haystack, needle)
976
 *
977
 *-------------------------------------------------------------------------
978
 */
979
char *
980
Wstrcasestr_wrap(const char *haystack, const char *needle)
981
{
982
    if (needle && !*needle)
983
        return (char *)haystack;
984
    else
985
        return StrStrIA(haystack, needle);
986
}
987
#endif /* H5_HAVE_WIN32_API */
988
989
/* dirname() and basename() are not easily ported to Windows and basename
990
 * behavior varies depending on if you get POSIX vs. GNU. As a more
991
 * platform-indpendent work-around, we've implemented H5_ versions of
992
 * dirname() and basename().
993
 *
994
 * - The input string is never modified.
995
 *
996
 * - The out parameter is a new string that was allocated with H5MM routines
997
 *   and must be freed by the caller via H5MM_free()/H5MM_xfree().
998
 *
999
 * - NULL pointers are errors.
1000
 *
1001
 * - On errors, FAIL will be returned and the output parameter will be
1002
 *   undefined.
1003
 *
1004
 * - Assumes the file separator is \ on Win32 and / everywhere else,
1005
 *   including Cygwin.
1006
 */
1007
1008
/*-------------------------------------------------------------------------
1009
 * Function:    H5_dirname
1010
 *
1011
 * Purpose:     Similar to dirname(3) but more portable across platforms.
1012
 *              Returns a pointer to the directory component of a specified
1013
 *              pathname. The returned pointer is allocated by this routine
1014
 *              and must be freed by the caller with H5MM_free or
1015
 *              H5MM_xfree.
1016
 *
1017
 * Return:      Non-negative on success/Negative on failure
1018
 *
1019
 *-------------------------------------------------------------------------
1020
 */
1021
herr_t
1022
H5_dirname(const char *path, char **dirname)
1023
0
{
1024
0
    char  *sep;
1025
0
    char  *out       = NULL;
1026
0
    herr_t ret_value = SUCCEED;
1027
1028
0
    FUNC_ENTER_NOAPI_NOINIT
1029
1030
0
    if (!path)
1031
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "path can't be NULL");
1032
0
    if (!dirname)
1033
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "dirname can't be NULL");
1034
1035
0
    if (NULL == (sep = strrchr(path, H5_DIR_SEPC))) {
1036
        /* Pathname with no file separator characters */
1037
0
        out = H5MM_strdup(".");
1038
0
    }
1039
0
    else if (sep == path) {
1040
        /* Pathname of form "/" or "/filename" */
1041
0
        out = H5MM_strdup(H5_DIR_SEPS);
1042
0
    }
1043
0
    else {
1044
0
        if (sep[1] == '\0') {
1045
            /*
1046
             * Last file separator character is last character in
1047
             * pathname. Skip this and any other preceding trailing
1048
             * file separator characters
1049
             */
1050
0
            while (sep != path && sep[-1] == H5_DIR_SEPC)
1051
0
                sep--;
1052
1053
0
            if (sep == path) {
1054
                /* Contrived case: "//", "///" and similar */
1055
0
                out = H5MM_strdup(H5_DIR_SEPS);
1056
0
                sep = NULL;
1057
0
            }
1058
0
            else {
1059
                /*
1060
                 * Must have found the filename component. Search
1061
                 * backwards to a previous file separator character,
1062
                 * if any.
1063
                 */
1064
0
                while (sep != path && sep[-1] != H5_DIR_SEPC)
1065
0
                    sep--;
1066
1067
0
                if (sep == path) {
1068
                    /* No directory component found, just return "." */
1069
0
                    out = H5MM_strdup(".");
1070
0
                    sep = NULL;
1071
0
                }
1072
0
            }
1073
0
        }
1074
1075
0
        if (sep) {
1076
0
            ptrdiff_t len;
1077
1078
            /* Skip a possible run of duplicate file separator characters */
1079
0
            while (sep != path && sep[-1] == H5_DIR_SEPC)
1080
0
                sep--;
1081
1082
0
            if (sep == path)
1083
                /* Pathname of form "/usr/" */
1084
0
                out = H5MM_strdup(H5_DIR_SEPS);
1085
0
            else {
1086
                /* Pathname of form "dir/filename" */
1087
0
                len = sep - path;
1088
0
                assert(len >= 0);
1089
1090
0
                out = H5MM_strndup(path, (size_t)len);
1091
0
            }
1092
0
        }
1093
0
    }
1094
1095
0
    if (NULL == out)
1096
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate buffer for dirname");
1097
1098
0
    *dirname = out;
1099
1100
0
done:
1101
0
    if (FAIL == ret_value) {
1102
0
        H5MM_free(out);
1103
0
        if (dirname)
1104
0
            *dirname = NULL;
1105
0
    }
1106
1107
0
    FUNC_LEAVE_NOAPI(ret_value)
1108
0
} /* end H5_dirname() */
1109
1110
/*-------------------------------------------------------------------------
1111
 * Function:    H5_basename
1112
 *
1113
 * Purpose:     Similar to basename(3) but more portable across platforms.
1114
 *              Returns a pointer to the filename component of a specified
1115
 *              pathname. The returned pointer is allocated by this routine
1116
 *              and must be freed by the caller with H5MM_free or
1117
 *              H5MM_xfree.
1118
 *
1119
 *              NOTE: This routine follows the POSIX semantics for
1120
 *              basename(3). That is, passing the path string "/" ("\" on
1121
 *              Windows) returns the string "/" (again, "\" on Windows) and
1122
 *              passing a path string with trailing file separator
1123
 *              characters returns the filename component with the trailing
1124
 *              file separator characters being ignored.
1125
 *
1126
 * Return:      Non-negative on success/Negative on failure
1127
 *
1128
 *-------------------------------------------------------------------------
1129
 */
1130
herr_t
1131
H5_basename(const char *path, char **basename)
1132
0
{
1133
0
    const char *sep;
1134
0
    char       *out       = NULL;
1135
0
    herr_t      ret_value = SUCCEED;
1136
1137
0
    FUNC_ENTER_NOAPI_NOINIT
1138
1139
0
    if (!path)
1140
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "path can't be NULL");
1141
0
    if (!basename)
1142
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "basename can't be NULL");
1143
1144
0
    if (NULL == (sep = strrchr(path, H5_DIR_SEPC))) {
1145
0
        if (*path == '\0')
1146
            /* Empty pathname */
1147
0
            out = H5MM_strdup(".");
1148
0
        else
1149
            /* Pathname with no file separator characters */
1150
0
            out = H5MM_strdup(path);
1151
0
    }
1152
0
    else if (sep == path) {
1153
0
        if (sep[1] == '\0')
1154
            /* Pathname of form "/" */
1155
0
            out = H5MM_strdup(H5_DIR_SEPS);
1156
0
        else
1157
            /* Pathname of form "/filename" */
1158
0
            out = H5MM_strdup(sep + 1);
1159
0
    }
1160
0
    else {
1161
0
        if (sep[1] != '\0')
1162
            /* Pathname of form "dir/filename" */
1163
0
            out = H5MM_strdup(sep + 1);
1164
0
        else {
1165
            /* Pathname of form "filename/", "/dir/filename/", etc. */
1166
1167
            /*
1168
             * Last file separator character is last character in
1169
             * pathname. Skip this and any other preceding trailing
1170
             * file separator characters
1171
             */
1172
0
            while (sep != path && sep[-1] == H5_DIR_SEPC)
1173
0
                sep--;
1174
1175
0
            if (sep == path)
1176
                /* Contrived case: "//", "///" and similar */
1177
0
                out = H5MM_strdup(H5_DIR_SEPS);
1178
0
            else {
1179
0
                const char *c_ptr = sep;
1180
0
                ptrdiff_t   len;
1181
1182
                /*
1183
                 * Skip back to a previous file separator character,
1184
                 * if any, and form final filename component
1185
                 */
1186
0
                while (c_ptr != path && c_ptr[-1] != H5_DIR_SEPC)
1187
0
                    c_ptr--;
1188
1189
0
                len = sep - c_ptr;
1190
0
                assert(len >= 0);
1191
1192
0
                out = H5MM_strndup(c_ptr, (size_t)len);
1193
0
            }
1194
0
        }
1195
0
    }
1196
1197
0
    if (NULL == out)
1198
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate buffer for basename");
1199
1200
0
    *basename = out;
1201
1202
0
done:
1203
0
    if (FAIL == ret_value) {
1204
0
        H5MM_free(out);
1205
0
        if (basename)
1206
0
            *basename = NULL;
1207
0
    }
1208
1209
0
    FUNC_LEAVE_NOAPI(ret_value)
1210
0
} /* end H5_basename() */
1211
1212
/* Global variables */
1213
int         H5_opterr = 1; /* Get_option prints errors if this is on */
1214
int         H5_optind = 1; /* Token pointer                          */
1215
const char *H5_optarg;     /* Flag argument (or value)               */
1216
1217
/*-------------------------------------------------------------------------
1218
 * Function: H5_get_option
1219
 *
1220
 * Purpose:  Determine the command-line options a user specified. We can
1221
 *           accept both short and long type command-lines.
1222
 *
1223
 * Return:  Success:    The short valued "name" of the command line
1224
 *                      parameter or EOF if there are no more
1225
 *                      parameters to process.
1226
 *
1227
 *          Failure:    A question mark.
1228
 *-------------------------------------------------------------------------
1229
 */
1230
int
1231
H5_get_option(int argc, const char *const *argv, const char *opts, const struct h5_long_options *l_opts)
1232
0
{
1233
0
    static int sp      = 1;   /* character index in current token */
1234
0
    int        optchar = '?'; /* option character passed back to user */
1235
1236
0
    if (sp == 1) {
1237
        /* check for more flag-like tokens */
1238
0
        if (H5_optind >= argc || argv[H5_optind][0] != '-' || argv[H5_optind][1] == '\0') {
1239
0
            return EOF;
1240
0
        }
1241
0
        else if (strcmp(argv[H5_optind], "--") == 0) {
1242
0
            H5_optind++;
1243
0
            return EOF;
1244
0
        }
1245
0
    }
1246
1247
0
    if (sp == 1 && argv[H5_optind][0] == '-' && argv[H5_optind][1] == '-') {
1248
        /* long command line option */
1249
0
        int        i;
1250
0
        const char ch      = '=';
1251
0
        char      *arg     = strdup(&argv[H5_optind][2]);
1252
0
        size_t     arg_len = 0;
1253
1254
0
        H5_optarg = strchr(&argv[H5_optind][2], ch);
1255
0
        arg_len   = strlen(&argv[H5_optind][2]);
1256
0
        if (H5_optarg) {
1257
0
            arg_len -= strlen(H5_optarg);
1258
0
            H5_optarg++; /* skip the equal sign */
1259
0
        }
1260
0
        arg[arg_len] = 0;
1261
1262
0
        for (i = 0; l_opts && l_opts[i].name; i++) {
1263
0
            if (strcmp(arg, l_opts[i].name) == 0) {
1264
                /* we've found a matching long command line flag */
1265
0
                optchar = l_opts[i].shortval;
1266
1267
0
                if (l_opts[i].has_arg != no_arg) {
1268
0
                    if (H5_optarg == NULL) {
1269
0
                        if (l_opts[i].has_arg != optional_arg) {
1270
0
                            if (H5_optind < (argc - 1))
1271
0
                                if (argv[H5_optind + 1][0] != '-')
1272
0
                                    H5_optarg = argv[++H5_optind];
1273
0
                        }
1274
0
                        else if (l_opts[i].has_arg == require_arg) {
1275
0
                            if (H5_opterr)
1276
0
                                fprintf(stderr, "%s: option required for \"--%s\" flag\n", argv[0], arg);
1277
1278
0
                            optchar = '?';
1279
0
                        }
1280
0
                    }
1281
0
                }
1282
0
                else {
1283
0
                    if (H5_optarg) {
1284
0
                        if (H5_opterr)
1285
0
                            fprintf(stderr, "%s: no option required for \"%s\" flag\n", argv[0], arg);
1286
1287
0
                        optchar = '?';
1288
0
                    }
1289
0
                }
1290
0
                break;
1291
0
            }
1292
0
        }
1293
1294
0
        if (l_opts && l_opts[i].name == NULL) {
1295
            /* exhausted all of the l_opts we have and still didn't match */
1296
0
            if (H5_opterr)
1297
0
                fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], arg);
1298
1299
0
            optchar = '?';
1300
0
        }
1301
1302
0
        H5_optind++;
1303
0
        sp = 1;
1304
1305
0
        free(arg);
1306
0
    }
1307
0
    else {
1308
0
        char *cp; /* pointer into current token */
1309
1310
        /* short command line option */
1311
0
        optchar = argv[H5_optind][sp];
1312
1313
0
        if (optchar == ':' || (cp = strchr(opts, optchar)) == 0) {
1314
0
            if (H5_opterr)
1315
0
                fprintf(stderr, "%s: unknown option \"%c\"\n", argv[0], optchar);
1316
1317
            /* if no chars left in this token, move to next token */
1318
0
            if (argv[H5_optind][++sp] == '\0') {
1319
0
                H5_optind++;
1320
0
                sp = 1;
1321
0
            }
1322
0
            return '?';
1323
0
        }
1324
1325
0
        if (*++cp == ':') {
1326
            /* if a value is expected, get it */
1327
0
            if (argv[H5_optind][sp + 1] != '\0') {
1328
                /* flag value is rest of current token */
1329
0
                H5_optarg = &argv[H5_optind++][sp + 1];
1330
0
            }
1331
0
            else if (++H5_optind >= argc) {
1332
0
                if (H5_opterr)
1333
0
                    fprintf(stderr, "%s: value expected for option \"%c\"\n", argv[0], optchar);
1334
1335
0
                optchar = '?';
1336
0
            }
1337
0
            else {
1338
                /* flag value is next token */
1339
0
                H5_optarg = argv[H5_optind++];
1340
0
            }
1341
1342
0
            sp = 1;
1343
0
        }
1344
        /* wildcard argument */
1345
0
        else if (*cp == '*') {
1346
            /* check the next argument */
1347
0
            H5_optind++;
1348
            /* we do have an extra argument, check if not last */
1349
0
            if ((H5_optind + 1) < argc) {
1350
0
                if (argv[H5_optind][0] != '-') {
1351
0
                    H5_optarg = argv[H5_optind++];
1352
0
                }
1353
0
                else {
1354
0
                    H5_optarg = NULL;
1355
0
                }
1356
0
            }
1357
0
            else {
1358
0
                H5_optarg = NULL;
1359
0
            }
1360
0
        }
1361
0
        else {
1362
            /* set up to look at next char in token, next time */
1363
0
            if (argv[H5_optind][++sp] == '\0') {
1364
                /* no more in current token, so setup next token */
1365
0
                H5_optind++;
1366
0
                sp = 1;
1367
0
            }
1368
0
            H5_optarg = NULL;
1369
0
        }
1370
0
    }
1371
1372
    /* return the current flag character found */
1373
0
    return optchar;
1374
0
}
1375
1376
/*-------------------------------------------------------------------------
1377
 * Function:    H5_strcasestr
1378
 *
1379
 * Purpose:     Find the first occurrence of the substring needle in the
1380
 *              string haystack ignoring case.
1381
 *
1382
 * Return:      Success:  Pointer to the beginning of the located substring
1383
 *
1384
 *              Failure:  NULL
1385
 *
1386
 *-------------------------------------------------------------------------
1387
 */
1388
char *
1389
H5_strcasestr(const char *haystack, const char *needle)
1390
0
{
1391
    /* Check arguments. */
1392
0
    assert(haystack);
1393
0
    assert(needle);
1394
1395
    /* begin once from each character of haystack, until needle is found */
1396
0
    do {
1397
0
        const char *h = haystack;
1398
0
        const char *n = needle;
1399
        /* loop while lowercase strings match, or needle ends */
1400
0
        while (tolower(*h) == tolower(*n) && *n) {
1401
0
            h++;
1402
0
            n++;
1403
0
        }
1404
        /* if all characters in needle matched we found it */
1405
0
        if (*n == 0) {
1406
            /* must discard const qualifier here, so turn off the warning */
1407
0
            H5_WARN_CAST_AWAY_CONST_OFF
1408
0
            return (char *)haystack;
1409
0
            H5_WARN_CAST_AWAY_CONST_ON
1410
0
        }
1411
0
    } while (*haystack++);
1412
0
    return 0;
1413
0
} /* end H5_strcasestr() */
1414
1415
/*
1416
 * HDqsort_context - Reentrant qsort with context parameter
1417
 *
1418
 * Provides a uniform interface to platform-specific reentrant qsort functions.
1419
 * All library code should use the GNU/Linux signature for comparator functions:
1420
 *
1421
 *   int comparator(const void *a, const void *b, void *context)
1422
 *
1423
 * The macro handles platform differences internally, allowing the same
1424
 * comparator signature to work on Windows (qsort_s), macOS (BSD qsort_r),
1425
 * FreeBSD (use BSD qsort_r < 14.0, but GNU qsort_r >= 14.0), and Linux (GNU qsort_r).
1426
 *
1427
 * Usage:
1428
 *   HDqsort_context(base, count, elem_size, compare_func, context);
1429
 */
1430
#if defined(H5_HAVE_WIN32_API) || defined(H5_HAVE_DARWIN) || (defined(__FreeBSD__) && __FreeBSD__ < 14)
1431
/* Need wrapper for Windows, macOS, and FreeBSD < 14 which expect context-first comparators */
1432
typedef struct HDqsort_context_wrapper_t {
1433
    int (*gnu_compar)(const void *, const void *, void *);
1434
    void *gnu_arg;
1435
} HDqsort_context_wrapper_t;
1436
1437
static int
1438
HDqsort_context_wrapper_func(void *wrapper_arg, const void *a, const void *b)
1439
{
1440
    HDqsort_context_wrapper_t *w = (HDqsort_context_wrapper_t *)wrapper_arg;
1441
    return w->gnu_compar(a, b, w->gnu_arg);
1442
}
1443
1444
herr_t
1445
HDqsort_context(void *base, size_t nel, size_t size, int (*compar)(const void *, const void *, void *),
1446
                void *arg)
1447
{
1448
    HDqsort_context_wrapper_t wrapper;
1449
    wrapper.gnu_compar = compar;
1450
    wrapper.gnu_arg    = arg;
1451
#if defined(H5_HAVE_WIN32_API)
1452
    qsort_s(base, nel, size, HDqsort_context_wrapper_func, &wrapper);
1453
#elif defined(H5_HAVE_DARWIN) || (defined(__FreeBSD__) && __FreeBSD__ < 14)
1454
    /* Old BSD-style: context parameter comes before comparator function */
1455
    qsort_r(base, nel, size, &wrapper, HDqsort_context_wrapper_func);
1456
#endif
1457
    return SUCCEED;
1458
}
1459
#endif
1460
1461
/*
1462
 * HDqsort_fallback - Fallback qsort implementation for platforms without qsort_r/qsort_s
1463
 *
1464
 * This implementation is not threadsafe, since it uses a global variable to store the
1465
 * comparator context, then uses standard qsort(). A beta branch of a threadsafe implementation
1466
 * of these routines may be found in the 'qsort_r_threadsafe' branch of the HDF5 GitHub repository.
1467
 *
1468
 */
1469
#ifndef H5_HAVE_QSORT_REENTRANT
1470
1471
typedef struct HDqsort_fallback_context_t {
1472
    int (*gnu_compar)(const void *, const void *, void *);
1473
    void *gnu_arg;
1474
} HDqsort_fallback_context_t;
1475
1476
/* Non-threadsafe: use global variable */
1477
static HDqsort_fallback_context_t *HDqsort_fallback_global_ctx = NULL;
1478
1479
static int
1480
HDqsort_fallback_wrapper(const void *a, const void *b)
1481
{
1482
    /* Call the original GNU-style comparator with context from global */
1483
    return HDqsort_fallback_global_ctx->gnu_compar(a, b, HDqsort_fallback_global_ctx->gnu_arg);
1484
}
1485
1486
herr_t
1487
HDqsort_fallback(void *base, size_t nel, size_t size, int (*compar)(const void *, const void *, void *),
1488
                 void *arg)
1489
{
1490
    HDqsort_fallback_context_t ctx;
1491
1492
    ctx.gnu_compar = compar;
1493
    ctx.gnu_arg    = arg;
1494
1495
    /* Store context in global variable */
1496
    HDqsort_fallback_global_ctx = &ctx;
1497
1498
    qsort(base, nel, size, HDqsort_fallback_wrapper);
1499
1500
    /* Clear the global pointer */
1501
    HDqsort_fallback_global_ctx = NULL;
1502
1503
    return SUCCEED;
1504
}
1505
1506
#endif /* !H5_HAVE_QSORT_REENTRANT */