blob: ea8a61840927a317f027e7cafea217f66c7abc73 [file] [log] [blame]
Michal Vasko5aa44c02020-06-29 11:47:02 +02001/**
2 * @file compat.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief compatibility functions
5 *
Michal Vaskod304a082021-05-19 16:36:10 +02006 * Copyright (c) 2021 CESNET, z.s.p.o.
Michal Vasko5aa44c02020-06-29 11:47:02 +02007 *
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 */
Radek Krejcif8dc59a2020-11-25 13:47:44 +010014#define _POSIX_C_SOURCE 200809L /* fdopen, _POSIX_PATH_MAX, strdup */
Michal Vasko5aa44c02020-06-29 11:47:02 +020015#define _ISOC99_SOURCE /* vsnprintf */
Michal Vasko5aa44c02020-06-29 11:47:02 +020016
Michal Vaskod304a082021-05-19 16:36:10 +020017#include "compat.h"
18
Michal Vasko5aa44c02020-06-29 11:47:02 +020019#include <errno.h>
Michal Vaskod304a082021-05-19 16:36:10 +020020#include <inttypes.h>
Michal Vasko5aa44c02020-06-29 11:47:02 +020021#include <limits.h>
Michal Vaskod304a082021-05-19 16:36:10 +020022#include <pthread.h>
Michal Vasko5aa44c02020-06-29 11:47:02 +020023#include <stdarg.h>
24#include <stdio.h>
25#include <stdlib.h>
Michal Vasko69730152020-10-09 16:30:07 +020026#include <string.h>
Jan Kundrát8ae30ea2021-12-09 23:37:02 +010027#ifndef _MSC_VER
Michal Vaskod304a082021-05-19 16:36:10 +020028#include <sys/time.h>
Jan Kundrát8ae30ea2021-12-09 23:37:02 +010029#endif
Michal Vaskod304a082021-05-19 16:36:10 +020030#include <time.h>
Michal Vasko5aa44c02020-06-29 11:47:02 +020031#include <unistd.h>
32
33#ifndef HAVE_VDPRINTF
34int
35vdprintf(int fd, const char *format, va_list ap)
36{
37 FILE *stream;
Radek Krejciafb28832020-12-01 12:16:28 +010038 int count = 0;
Michal Vasko5aa44c02020-06-29 11:47:02 +020039
40 stream = fdopen(dup(fd), "a+");
41 if (stream) {
42 count = vfprintf(stream, format, ap);
43 fclose(stream);
44 }
45 return count;
46}
Radek Krejci0f969882020-08-21 16:56:47 +020047
Michal Vasko5aa44c02020-06-29 11:47:02 +020048#endif
49
50#ifndef HAVE_ASPRINTF
51int
52asprintf(char **strp, const char *fmt, ...)
53{
54 int ret;
55 va_list ap;
56
57 va_start(ap, fmt);
58 ret = vasprintf(strp, fmt, ap);
59 va_end(ap);
60 return ret;
61}
Radek Krejci0f969882020-08-21 16:56:47 +020062
Michal Vasko5aa44c02020-06-29 11:47:02 +020063#endif
64
65#ifndef HAVE_VASPRINTF
66int
67vasprintf(char **strp, const char *fmt, va_list ap)
68{
69 va_list ap2;
Michal Vasko69730152020-10-09 16:30:07 +020070
Michal Vasko5aa44c02020-06-29 11:47:02 +020071 va_copy(ap2, ap);
72 int l = vsnprintf(0, 0, fmt, ap2);
Michal Vasko69730152020-10-09 16:30:07 +020073
Michal Vasko5aa44c02020-06-29 11:47:02 +020074 va_end(ap2);
75
Michal Vasko69730152020-10-09 16:30:07 +020076 if ((l < 0) || !(*strp = malloc(l + 1U))) {
Michal Vasko5aa44c02020-06-29 11:47:02 +020077 return -1;
78 }
79
80 return vsnprintf(*strp, l + 1U, fmt, ap);
81}
Radek Krejci0f969882020-08-21 16:56:47 +020082
Michal Vasko5aa44c02020-06-29 11:47:02 +020083#endif
84
Michal Vaskod304a082021-05-19 16:36:10 +020085#ifndef HAVE_GETLINE
86ssize_t
87getline(char **lineptr, size_t *n, FILE *stream)
88{
lePicif71fd7d2023-07-04 08:07:01 +020089 static char chunk[256];
Michal Vaskod304a082021-05-19 16:36:10 +020090 char *ptr;
lePicif71fd7d2023-07-04 08:07:01 +020091 ssize_t len, written;
Michal Vaskod304a082021-05-19 16:36:10 +020092
93 if (!lineptr || !n) {
94 errno = EINVAL;
95 return -1;
96 }
97
98 if (ferror(stream) || feof(stream)) {
99 return -1;
100 }
101
lePicif71fd7d2023-07-04 08:07:01 +0200102 *n = *lineptr ? *n : 0;
103 written = 0;
104 while (fgets(chunk, sizeof(chunk), stream)) {
105 len = strlen(chunk);
Michal Vasko21eaa392024-02-20 15:48:42 +0100106 if ((size_t)(written + len) > *n) {
lePicif71fd7d2023-07-04 08:07:01 +0200107 ptr = realloc(*lineptr, *n + sizeof(chunk));
108 if (!ptr) {
109 return -1;
110 }
111 *lineptr = ptr;
112 *n = *n + sizeof(chunk);
Michal Vaskod304a082021-05-19 16:36:10 +0200113 }
lePicif71fd7d2023-07-04 08:07:01 +0200114 memcpy(*lineptr + written, &chunk, len);
115 written += len;
116 if ((*lineptr)[written - 1] == '\n') {
117 break;
118 }
119 }
120 if (written) {
121 (*lineptr)[written] = '\0';
122 } else {
123 written = -1;
Michal Vaskod304a082021-05-19 16:36:10 +0200124 }
125
lePicif71fd7d2023-07-04 08:07:01 +0200126 return written;
Michal Vaskod304a082021-05-19 16:36:10 +0200127}
128
129#endif
130
Michal Vasko5aa44c02020-06-29 11:47:02 +0200131#ifndef HAVE_STRNDUP
132char *
133strndup(const char *s, size_t n)
134{
135 char *buf;
136 size_t len = 0;
137
138 /* strnlen */
Michal Vaskod989ba02020-08-24 10:59:24 +0200139 for ( ; (len < n) && (s[len] != '\0'); ++len) {}
Michal Vasko5aa44c02020-06-29 11:47:02 +0200140
141 if (!(buf = malloc(len + 1U))) {
142 return NULL;
143 }
144
145 memcpy(buf, s, len);
146 buf[len] = '\0';
147 return buf;
148}
Radek Krejci0f969882020-08-21 16:56:47 +0200149
Michal Vasko5aa44c02020-06-29 11:47:02 +0200150#endif
151
152#ifndef HAVE_STRNSTR
153char *
154strnstr(const char *s, const char *find, size_t slen)
155{
156 char c, sc;
157 size_t len;
158
159 if ((c = *find++) != '\0') {
160 len = strlen(find);
161 do {
162 do {
163 if ((slen-- < 1) || ((sc = *s++) == '\0')) {
164 return NULL;
165 }
166 } while (sc != c);
167 if (len > slen) {
168 return NULL;
169 }
170 } while (strncmp(s, find, len));
171 s--;
172 }
173 return (char *)s;
174}
Radek Krejci0f969882020-08-21 16:56:47 +0200175
Michal Vasko5aa44c02020-06-29 11:47:02 +0200176#endif
177
Michal Vaskod304a082021-05-19 16:36:10 +0200178#ifndef HAVE_STRCHRNUL
179char *
180strchrnul(const char *s, int c)
Michal Vasko5aa44c02020-06-29 11:47:02 +0200181{
Michal Vaskod304a082021-05-19 16:36:10 +0200182 char *p = strchr(s, c);
Michal Vasko45c4b1a2021-05-19 16:48:12 +0200183
Michal Vaskod304a082021-05-19 16:36:10 +0200184 return p ? p : (char *)s + strlen(s);
Michal Vasko5aa44c02020-06-29 11:47:02 +0200185}
Radek Krejci0f969882020-08-21 16:56:47 +0200186
Michal Vasko5aa44c02020-06-29 11:47:02 +0200187#endif
188
189#ifndef HAVE_GET_CURRENT_DIR_NAME
190char *
191get_current_dir_name(void)
192{
193 char tmp[_POSIX_PATH_MAX];
194 char *retval = NULL;
195
196 if (getcwd(tmp, sizeof(tmp))) {
197 retval = strdup(tmp);
198 if (!retval) {
199 errno = ENOMEM;
200 }
201 }
202
203 return retval;
204}
Radek Krejci0f969882020-08-21 16:56:47 +0200205
Michal Vasko5aa44c02020-06-29 11:47:02 +0200206#endif
Michal Vaskod304a082021-05-19 16:36:10 +0200207
Jan Kundráte2354142021-12-10 16:29:21 +0100208#ifndef _MSC_VER
Michal Vaskod304a082021-05-19 16:36:10 +0200209#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
210int
211pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
212{
213 int64_t nsec_diff;
214 int32_t diff;
215 struct timespec cur, dur;
216 int rc;
217
218 /* try to acquire the lock and, if we fail, sleep for 5ms. */
219 while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
220 /* get real time */
221#ifdef CLOCK_REALTIME
222 clock_gettime(CLOCK_REALTIME, &cur);
223#else
224 struct timeval tv;
225
226 gettimeofday(&tv, NULL);
227 cur.tv_sec = (time_t)tv.tv_sec;
228 cur.tv_nsec = 1000L * (long)tv.tv_usec;
229#endif
230
231 /* get time diff */
232 nsec_diff = 0;
233 nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L;
234 nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec);
235 diff = (nsec_diff ? nsec_diff / 1000000L : 0);
236
237 if (diff < 1) {
238 /* timeout */
239 break;
240 } else if (diff < 5) {
241 /* sleep until timeout */
242 dur.tv_sec = 0;
243 dur.tv_nsec = (long)diff * 1000000;
244 } else {
245 /* sleep 5 ms */
246 dur.tv_sec = 0;
247 dur.tv_nsec = 5000000;
248 }
249
250 nanosleep(&dur, NULL);
251 }
252
253 return rc;
254}
255
256#endif
Jan Kundráte2354142021-12-10 16:29:21 +0100257#endif
Jan Kundrát147a72c2021-12-10 16:13:57 +0100258
259#ifndef HAVE_REALPATH
260#ifdef _WIN32
261char *
262realpath(const char *path, char *resolved_path)
263{
264 char *resolved = _fullpath(resolved_path, path, PATH_MAX);
265
266 if ((_access(resolved, 0) == -1) && (errno == ENOENT)) {
267 return NULL;
268 }
269 return resolved;
270}
271
Donatas Abraitisd5d533c2022-06-10 08:22:25 +0300272#elif defined (__NetBSD__)
273char *
274realpath(const char *path, char *resolved_path)
275{
276 ssize_t nbytes;
277
278 nbytes = readlink(path, resolved_path, PATH_MAX);
279 if (nbytes == -1) {
280 return NULL;
281 }
282 return resolved_path;
283}
284
Jan Kundrát147a72c2021-12-10 16:13:57 +0100285#else
286#error No realpath() implementation for this platform is available.
287#endif
288#endif
Jan Kundrát91907632021-12-14 16:18:16 +0100289
290#ifndef HAVE_LOCALTIME_R
291#ifdef _WIN32
292struct tm *
293localtime_r(const time_t *timep, struct tm *result)
294{
295 errno_t res = localtime_s(result, timep);
296
297 if (res) {
298 return NULL;
299 } else {
300 return result;
301 }
302}
303
304#else
305#error No localtime_r() implementation for this platform is available.
306#endif
307#endif
Jan Kundrát406e96e2021-12-11 19:37:33 +0100308
Jan Kundráta9caaf72022-02-14 18:29:43 +0100309#ifndef HAVE_GMTIME_R
310#ifdef _WIN32
311struct tm *
312gmtime_r(const time_t *timep, struct tm *result)
313{
314 errno_t res = gmtime_s(result, timep);
315
316 if (res) {
317 return NULL;
318 } else {
319 return result;
320 }
321}
322
323#else
324#error No gmtime_r() implementation for this platform is available.
325#endif
326#endif
327
Michal Vasko42948332024-02-20 15:47:01 +0100328#ifndef HAVE_TIMEGM
329time_t
330timegm(struct tm *tm)
331{
332 pthread_mutex_t tz_lock = PTHREAD_MUTEX_INITIALIZER;
333 time_t ret;
334 char *tz;
335
336 pthread_mutex_lock(&tz_lock);
337
338 tz = getenv("TZ");
339 if (tz) {
340 tz = strdup(tz);
341 }
342 setenv("TZ", "", 1);
343 tzset();
344
345 ret = mktime(tm);
346
347 if (tz) {
348 setenv("TZ", tz, 1);
349 free(tz);
350 } else {
351 unsetenv("TZ");
352 }
353 tzset();
354
355 pthread_mutex_unlock(&tz_lock);
356
357 return ret;
358}
359
360#endif
361
Jan Kundrátc8805302021-12-11 23:40:26 +0100362#ifndef HAVE_SETENV
363#ifdef _WIN32
364int
365setenv(const char *name, const char *value, int overwrite)
366{
367 int errcode = 0;
368
369 if (!overwrite) {
370 size_t envsize = 0;
Michal Vasko26bbb272022-08-02 14:54:33 +0200371
Jan Kundrátc8805302021-12-11 23:40:26 +0100372 errcode = getenv_s(&envsize, NULL, 0, name);
373 if (errcode || envsize) {
374 return errcode;
375 }
376 }
377 return _putenv_s(name, value);
378}
379
380#else
381#error No setenv() implementation for this platform is available.
382#endif
383#endif