Coverage Report

Created: 2025-10-10 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/compat/compat.c
Line
Count
Source
1
/**
2
 * @file compat.c
3
 * @author Michal Vasko <mvasko@cesnet.cz>
4
 * @brief compatibility functions
5
 *
6
 * Copyright (c) 2021 CESNET, z.s.p.o.
7
 *
8
 * This source code is licensed under BSD 3-Clause License (the "License").
9
 * You may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     https://opensource.org/licenses/BSD-3-Clause
13
 */
14
#ifndef _POSIX_C_SOURCE
15
# define _POSIX_C_SOURCE 200809L /* fdopen, _POSIX_PATH_MAX, strdup */
16
#endif
17
#define _ISOC99_SOURCE /* vsnprintf */
18
19
#include "compat.h"
20
21
#include <errno.h>
22
#include <inttypes.h>
23
#include <limits.h>
24
#include <pthread.h>
25
#include <stdarg.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#ifndef _MSC_VER
30
#include <sys/time.h>
31
#endif
32
#include <time.h>
33
#include <unistd.h>
34
35
#ifndef HAVE_VDPRINTF
36
int
37
vdprintf(int fd, const char *format, va_list ap)
38
{
39
    FILE *stream;
40
    int count = 0;
41
42
    stream = fdopen(dup(fd), "a+");
43
    if (stream) {
44
        count = vfprintf(stream, format, ap);
45
        fclose(stream);
46
    }
47
    return count;
48
}
49
50
#endif
51
52
#ifndef HAVE_ASPRINTF
53
int
54
asprintf(char **strp, const char *fmt, ...)
55
{
56
    int ret;
57
    va_list ap;
58
59
    va_start(ap, fmt);
60
    ret = vasprintf(strp, fmt, ap);
61
    va_end(ap);
62
    return ret;
63
}
64
65
#endif
66
67
#ifndef HAVE_VASPRINTF
68
int
69
vasprintf(char **strp, const char *fmt, va_list ap)
70
{
71
    va_list ap2;
72
73
    va_copy(ap2, ap);
74
    int l = vsnprintf(0, 0, fmt, ap2);
75
76
    va_end(ap2);
77
78
    if ((l < 0) || !(*strp = malloc(l + 1U))) {
79
        return -1;
80
    }
81
82
    return vsnprintf(*strp, l + 1U, fmt, ap);
83
}
84
85
#endif
86
87
#ifndef HAVE_GETLINE
88
ssize_t
89
getline(char **lineptr, size_t *n, FILE *stream)
90
{
91
    static char chunk[256];
92
    char *ptr;
93
    ssize_t len, written;
94
95
    if (!lineptr || !n) {
96
        errno = EINVAL;
97
        return -1;
98
    }
99
100
    if (ferror(stream) || feof(stream)) {
101
        return -1;
102
    }
103
104
    *n = *lineptr ? *n : 0;
105
    written = 0;
106
    while (fgets(chunk, sizeof(chunk), stream)) {
107
        len = strlen(chunk);
108
        if ((size_t)(written + len) > *n) {
109
            ptr = realloc(*lineptr, *n + sizeof(chunk));
110
            if (!ptr) {
111
                return -1;
112
            }
113
            *lineptr = ptr;
114
            *n = *n + sizeof(chunk);
115
        }
116
        memcpy(*lineptr + written, &chunk, len);
117
        written += len;
118
        if ((*lineptr)[written - 1] == '\n') {
119
            break;
120
        }
121
    }
122
    if (written) {
123
        (*lineptr)[written] = '\0';
124
    } else {
125
        written = -1;
126
    }
127
128
    return written;
129
}
130
131
#endif
132
133
#ifndef HAVE_STRNDUP
134
char *
135
strndup(const char *s, size_t n)
136
{
137
    char *buf;
138
    size_t len = 0;
139
140
    /* strnlen */
141
    for ( ; (len < n) && (s[len] != '\0'); ++len) {}
142
143
    if (!(buf = malloc(len + 1U))) {
144
        return NULL;
145
    }
146
147
    memcpy(buf, s, len);
148
    buf[len] = '\0';
149
    return buf;
150
}
151
152
#endif
153
154
#ifndef HAVE_STRNSTR
155
char *
156
strnstr(const char *s, const char *find, size_t slen)
157
345k
{
158
345k
    char c, sc;
159
345k
    size_t len;
160
161
345k
    if ((c = *find++) != '\0') {
162
345k
        len = strlen(find);
163
345k
        do {
164
5.96M
            do {
165
5.96M
                if ((slen-- < 1) || ((sc = *s++) == '\0')) {
166
229k
                    return NULL;
167
229k
                }
168
5.96M
            } while (sc != c);
169
116k
            if (len > slen) {
170
0
                return NULL;
171
0
            }
172
116k
        } while (strncmp(s, find, len));
173
116k
        s--;
174
116k
    }
175
116k
    return (char *)s;
176
345k
}
177
178
#endif
179
180
#ifndef HAVE_STRCHRNUL
181
char *
182
strchrnul(const char *s, int c)
183
{
184
    char *p = strchr(s, c);
185
186
    return p ? p : (char *)s + strlen(s);
187
}
188
189
#endif
190
191
#ifndef HAVE_GET_CURRENT_DIR_NAME
192
char *
193
get_current_dir_name(void)
194
{
195
    char tmp[_POSIX_PATH_MAX];
196
    char *retval = NULL;
197
198
    if (getcwd(tmp, sizeof(tmp))) {
199
        retval = strdup(tmp);
200
        if (!retval) {
201
            errno = ENOMEM;
202
        }
203
    }
204
205
    return retval;
206
}
207
208
#endif
209
210
#ifndef _MSC_VER
211
#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
212
int
213
pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
214
{
215
    int64_t nsec_diff;
216
    int32_t diff;
217
    struct timespec cur, dur;
218
    int rc;
219
220
    /* try to acquire the lock and, if we fail, sleep for 5ms. */
221
    while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
222
        /* get real time */
223
#ifdef CLOCK_REALTIME
224
        clock_gettime(CLOCK_REALTIME, &cur);
225
#else
226
        struct timeval tv;
227
228
        gettimeofday(&tv, NULL);
229
        cur.tv_sec = (time_t)tv.tv_sec;
230
        cur.tv_nsec = 1000L * (long)tv.tv_usec;
231
#endif
232
233
        /* get time diff */
234
        nsec_diff = 0;
235
        nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L;
236
        nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec);
237
        diff = (nsec_diff ? nsec_diff / 1000000L : 0);
238
239
        if (diff < 1) {
240
            /* timeout */
241
            break;
242
        } else if (diff < 5) {
243
            /* sleep until timeout */
244
            dur.tv_sec = 0;
245
            dur.tv_nsec = (long)diff * 1000000;
246
        } else {
247
            /* sleep 5 ms */
248
            dur.tv_sec = 0;
249
            dur.tv_nsec = 5000000;
250
        }
251
252
        nanosleep(&dur, NULL);
253
    }
254
255
    return rc;
256
}
257
258
#endif
259
#endif
260
261
#ifndef HAVE_REALPATH
262
#ifdef _WIN32
263
char *
264
realpath(const char *path, char *resolved_path)
265
{
266
    char *resolved = _fullpath(resolved_path, path, PATH_MAX);
267
268
    if ((_access(resolved, 0) == -1) && (errno == ENOENT)) {
269
        return NULL;
270
    }
271
    return resolved;
272
}
273
274
#elif defined (__NetBSD__)
275
char *
276
realpath(const char *path, char *resolved_path)
277
{
278
    ssize_t nbytes;
279
280
    nbytes = readlink(path, resolved_path, PATH_MAX);
281
    if (nbytes == -1) {
282
        return NULL;
283
    }
284
    return resolved_path;
285
}
286
287
#else
288
#error No realpath() implementation for this platform is available.
289
#endif
290
#endif
291
292
#ifndef HAVE_LOCALTIME_R
293
#ifdef _WIN32
294
struct tm *
295
localtime_r(const time_t *timep, struct tm *result)
296
{
297
    errno_t res = localtime_s(result, timep);
298
299
    if (res) {
300
        return NULL;
301
    } else {
302
        return result;
303
    }
304
}
305
306
#else
307
#error No localtime_r() implementation for this platform is available.
308
#endif
309
#endif
310
311
#ifndef HAVE_GMTIME_R
312
#ifdef _WIN32
313
struct tm *
314
gmtime_r(const time_t *timep, struct tm *result)
315
{
316
    errno_t res = gmtime_s(result, timep);
317
318
    if (res) {
319
        return NULL;
320
    } else {
321
        return result;
322
    }
323
}
324
325
#else
326
#error No gmtime_r() implementation for this platform is available.
327
#endif
328
#endif
329
330
#ifndef HAVE_TIMEGM
331
time_t
332
timegm(struct tm *tm)
333
{
334
    pthread_mutex_t tz_lock = PTHREAD_MUTEX_INITIALIZER;
335
    time_t ret;
336
    char *tz;
337
338
    pthread_mutex_lock(&tz_lock);
339
340
    tz = getenv("TZ");
341
    if (tz) {
342
        tz = strdup(tz);
343
    }
344
    setenv("TZ", "", 1);
345
    tzset();
346
347
    ret = mktime(tm);
348
349
    if (tz) {
350
        setenv("TZ", tz, 1);
351
        free(tz);
352
    } else {
353
        unsetenv("TZ");
354
    }
355
    tzset();
356
357
    pthread_mutex_unlock(&tz_lock);
358
359
    return ret;
360
}
361
362
#endif
363
364
#ifndef HAVE_SETENV
365
#ifdef _WIN32
366
int
367
setenv(const char *name, const char *value, int overwrite)
368
{
369
    int errcode = 0;
370
371
    if (!overwrite) {
372
        size_t envsize = 0;
373
374
        errcode = getenv_s(&envsize, NULL, 0, name);
375
        if (errcode || envsize) {
376
            return errcode;
377
        }
378
    }
379
    return _putenv_s(name, value);
380
}
381
382
#else
383
#error No setenv() implementation for this platform is available.
384
#endif
385
#endif