summaryrefslogtreecommitdiffstats
path: root/compat
diff options
context:
space:
mode:
Diffstat (limited to 'compat')
-rwxr-xr-xcompat/check_includes.sh44
-rw-r--r--compat/compat.c361
-rw-r--r--compat/compat.h.in203
-rw-r--r--compat/posix-shims/libgen.h1
-rw-r--r--compat/posix-shims/strings.h9
-rw-r--r--compat/posix-shims/unistd.h78
-rw-r--r--compat/strptime.c214
7 files changed, 910 insertions, 0 deletions
diff --git a/compat/check_includes.sh b/compat/check_includes.sh
new file mode 100755
index 0000000..0090007
--- /dev/null
+++ b/compat/check_includes.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+RETVAL=0
+
+# params - paths to the source files to search
+SRC="$*"
+
+# param FUNC - name of the function in compat to check
+check_compat_func () {
+ FILES=`grep -rE "([^[:alnum:]]|^)$1\([^\)]+\)" --include=\*.{c,h} $SRC | cut -d: -f1 | uniq`
+ for f in $FILES; do
+ grep -q "#include \"compat.h\"" $f
+ if [ $? -ne 0 ]; then
+ echo "Missing #include \"compat.h\" in file $f for function $1()"
+ RETVAL=$((RETVAL+1))
+ fi
+ done
+}
+
+check_compat_macro () {
+ FILES=`grep -rE "([^[:alnum:]]|^)$1([^[:alnum:]]|$)" --include=\*.{c,h} $SRC | cut -d: -f1 | uniq`
+ for f in $FILES; do
+ grep -q "#include \"compat.h\"" $f
+ if [ $? -ne 0 ]; then
+ echo "Missing #include \"compat.h\" in file $f for macro $1"
+ RETVAL=$((RETVAL+1))
+ fi
+ done
+}
+
+check_compat_func vdprintf
+check_compat_func asprintf
+check_compat_func vasprintf
+check_compat_func getline
+check_compat_func strndup
+check_compat_func strnstr
+check_compat_func strdupa
+check_compat_func strchrnul
+check_compat_func get_current_dir_name
+check_compat_func pthread_mutex_timedlock
+check_compat_func UNUSED
+check_compat_macro _PACKED
+
+exit $RETVAL
diff --git a/compat/compat.c b/compat/compat.c
new file mode 100644
index 0000000..5fb2be8
--- /dev/null
+++ b/compat/compat.c
@@ -0,0 +1,361 @@
+/**
+ * @file compat.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief compatibility functions
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _POSIX_C_SOURCE 200809L /* fdopen, _POSIX_PATH_MAX, strdup */
+#define _ISOC99_SOURCE /* vsnprintf */
+
+#include "compat.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _MSC_VER
+#include <sys/time.h>
+#endif
+#include <time.h>
+#include <unistd.h>
+
+#ifndef HAVE_VDPRINTF
+int
+vdprintf(int fd, const char *format, va_list ap)
+{
+ FILE *stream;
+ int count = 0;
+
+ stream = fdopen(dup(fd), "a+");
+ if (stream) {
+ count = vfprintf(stream, format, ap);
+ fclose(stream);
+ }
+ return count;
+}
+
+#endif
+
+#ifndef HAVE_ASPRINTF
+int
+asprintf(char **strp, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vasprintf(strp, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+#endif
+
+#ifndef HAVE_VASPRINTF
+int
+vasprintf(char **strp, const char *fmt, va_list ap)
+{
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ int l = vsnprintf(0, 0, fmt, ap2);
+
+ va_end(ap2);
+
+ if ((l < 0) || !(*strp = malloc(l + 1U))) {
+ return -1;
+ }
+
+ return vsnprintf(*strp, l + 1U, fmt, ap);
+}
+
+#endif
+
+#ifndef HAVE_GETLINE
+ssize_t
+getline(char **lineptr, size_t *n, FILE *stream)
+{
+ static char line[256];
+ char *ptr;
+ ssize_t len;
+
+ if (!lineptr || !n) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ferror(stream) || feof(stream)) {
+ return -1;
+ }
+
+ if (!fgets(line, 256, stream)) {
+ return -1;
+ }
+
+ ptr = strchr(line, '\n');
+ if (ptr) {
+ *ptr = '\0';
+ }
+
+ len = strlen(line);
+
+ if (len + 1 < 256) {
+ ptr = realloc(*lineptr, 256);
+ if (!ptr) {
+ return -1;
+ }
+ *lineptr = ptr;
+ *n = 256;
+ }
+
+ strcpy(*lineptr, line);
+ return len;
+}
+
+#endif
+
+#ifndef HAVE_STRNDUP
+char *
+strndup(const char *s, size_t n)
+{
+ char *buf;
+ size_t len = 0;
+
+ /* strnlen */
+ for ( ; (len < n) && (s[len] != '\0'); ++len) {}
+
+ if (!(buf = malloc(len + 1U))) {
+ return NULL;
+ }
+
+ memcpy(buf, s, len);
+ buf[len] = '\0';
+ return buf;
+}
+
+#endif
+
+#ifndef HAVE_STRNSTR
+char *
+strnstr(const char *s, const char *find, size_t slen)
+{
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != '\0') {
+ len = strlen(find);
+ do {
+ do {
+ if ((slen-- < 1) || ((sc = *s++) == '\0')) {
+ return NULL;
+ }
+ } while (sc != c);
+ if (len > slen) {
+ return NULL;
+ }
+ } while (strncmp(s, find, len));
+ s--;
+ }
+ return (char *)s;
+}
+
+#endif
+
+#ifndef HAVE_STRCHRNUL
+char *
+strchrnul(const char *s, int c)
+{
+ char *p = strchr(s, c);
+
+ return p ? p : (char *)s + strlen(s);
+}
+
+#endif
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+char *
+get_current_dir_name(void)
+{
+ char tmp[_POSIX_PATH_MAX];
+ char *retval = NULL;
+
+ if (getcwd(tmp, sizeof(tmp))) {
+ retval = strdup(tmp);
+ if (!retval) {
+ errno = ENOMEM;
+ }
+ }
+
+ return retval;
+}
+
+#endif
+
+#ifndef _MSC_VER
+#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
+int
+pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+ int64_t nsec_diff;
+ int32_t diff;
+ struct timespec cur, dur;
+ int rc;
+
+ /* try to acquire the lock and, if we fail, sleep for 5ms. */
+ while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
+ /* get real time */
+#ifdef CLOCK_REALTIME
+ clock_gettime(CLOCK_REALTIME, &cur);
+#else
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ cur.tv_sec = (time_t)tv.tv_sec;
+ cur.tv_nsec = 1000L * (long)tv.tv_usec;
+#endif
+
+ /* get time diff */
+ nsec_diff = 0;
+ nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L;
+ nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec);
+ diff = (nsec_diff ? nsec_diff / 1000000L : 0);
+
+ if (diff < 1) {
+ /* timeout */
+ break;
+ } else if (diff < 5) {
+ /* sleep until timeout */
+ dur.tv_sec = 0;
+ dur.tv_nsec = (long)diff * 1000000;
+ } else {
+ /* sleep 5 ms */
+ dur.tv_sec = 0;
+ dur.tv_nsec = 5000000;
+ }
+
+ nanosleep(&dur, NULL);
+ }
+
+ return rc;
+}
+
+#endif
+#endif
+
+#ifndef HAVE_REALPATH
+#ifdef _WIN32
+char *
+realpath(const char *path, char *resolved_path)
+{
+ char *resolved = _fullpath(resolved_path, path, PATH_MAX);
+
+ if ((_access(resolved, 0) == -1) && (errno == ENOENT)) {
+ return NULL;
+ }
+ return resolved;
+}
+
+#elif defined (__NetBSD__)
+char *
+realpath(const char *path, char *resolved_path)
+{
+ ssize_t nbytes;
+
+ nbytes = readlink(path, resolved_path, PATH_MAX);
+ if (nbytes == -1) {
+ return NULL;
+ }
+ return resolved_path;
+}
+
+#else
+#error No realpath() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+#ifdef _WIN32
+struct tm *
+localtime_r(const time_t *timep, struct tm *result)
+{
+ errno_t res = localtime_s(result, timep);
+
+ if (res) {
+ return NULL;
+ } else {
+ return result;
+ }
+}
+
+#else
+#error No localtime_r() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_GMTIME_R
+#ifdef _WIN32
+struct tm *
+gmtime_r(const time_t *timep, struct tm *result)
+{
+ errno_t res = gmtime_s(result, timep);
+
+ if (res) {
+ return NULL;
+ } else {
+ return result;
+ }
+}
+
+#else
+#error No gmtime_r() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_DIRNAME
+#ifdef _WIN32
+#include <shlwapi.h>
+char *
+dirname(char *path)
+{
+ PathRemoveFileSpecA(path);
+ return path;
+}
+
+#else
+#error No dirname() implementation for this platform is available.
+#endif
+#endif
+
+#ifndef HAVE_SETENV
+#ifdef _WIN32
+int
+setenv(const char *name, const char *value, int overwrite)
+{
+ int errcode = 0;
+
+ if (!overwrite) {
+ size_t envsize = 0;
+
+ errcode = getenv_s(&envsize, NULL, 0, name);
+ if (errcode || envsize) {
+ return errcode;
+ }
+ }
+ return _putenv_s(name, value);
+}
+
+#else
+#error No setenv() implementation for this platform is available.
+#endif
+#endif
diff --git a/compat/compat.h.in b/compat/compat.h.in
new file mode 100644
index 0000000..c697d6c
--- /dev/null
+++ b/compat/compat.h.in
@@ -0,0 +1,203 @@
+/**
+ * @file compat.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief compatibility functions header
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef _COMPAT_H_
+#define _COMPAT_H_
+
+#ifdef _WIN32
+/* headers are broken on Windows, which means that some of them simply *have* to come first */
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#endif
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef __WORDSIZE
+# if defined __x86_64__ && !defined __ILP32__
+# define __WORDSIZE 64
+# else
+# define __WORDSIZE 32
+# endif
+#endif
+
+#ifndef __INT64_C
+# if __WORDSIZE == 64
+# define __INT64_C(c) c ## L
+# define __UINT64_C(c) c ## UL
+# else
+# define __INT64_C(c) c ## LL
+# define __UINT64_C(c) c ## ULL
+# endif
+#endif
+
+#if defined (__GNUC__) || defined (__llvm__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+# define _PACKED __attribute__((__packed__))
+#else
+# define UNUSED(x) UNUSED_ ## x
+# define _PACKED
+#endif
+
+#cmakedefine HAVE_VDPRINTF
+#cmakedefine HAVE_ASPRINTF
+#cmakedefine HAVE_VASPRINTF
+#cmakedefine HAVE_GETLINE
+#cmakedefine HAVE_STRNDUP
+#cmakedefine HAVE_STRNSTR
+#cmakedefine HAVE_STRDUPA
+#cmakedefine HAVE_STRCHRNUL
+#cmakedefine HAVE_GET_CURRENT_DIR_NAME
+#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK
+#cmakedefine HAVE_TM_GMTOFF
+#cmakedefine HAVE_TIME_H_TIMEZONE
+#cmakedefine HAVE_REALPATH
+#cmakedefine HAVE_LOCALTIME_R
+#cmakedefine HAVE_GMTIME_R
+#cmakedefine HAVE_STRPTIME
+#cmakedefine HAVE_MMAP
+#cmakedefine HAVE_DIRNAME
+#cmakedefine HAVE_STRCASECMP
+#cmakedefine HAVE_SETENV
+
+#ifndef bswap64
+#define bswap64(val) \
+ ( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \
+ (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) | \
+ (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \
+ (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) )
+#endif
+
+#undef le64toh
+#undef htole64
+
+#cmakedefine IS_BIG_ENDIAN
+
+#ifdef IS_BIG_ENDIAN
+# define le64toh(x) bswap64(x)
+# define htole64(x) bswap64(x)
+#else
+# define le64toh(x) (x)
+# define htole64(x) (x)
+#endif
+
+#cmakedefine HAVE_STDATOMIC
+
+#ifdef HAVE_STDATOMIC
+# include <stdatomic.h>
+
+# define ATOMIC_T atomic_uint_fast32_t
+# define ATOMIC_T_MAX UINT_FAST32_MAX
+
+# define ATOMIC_STORE_RELAXED(var, x) atomic_store_explicit(&(var), x, memory_order_relaxed)
+# define ATOMIC_LOAD_RELAXED(var) atomic_load_explicit(&(var), memory_order_relaxed)
+# define ATOMIC_INC_RELAXED(var) atomic_fetch_add_explicit(&(var), 1, memory_order_relaxed)
+# define ATOMIC_ADD_RELAXED(var, x) atomic_fetch_add_explicit(&(var), x, memory_order_relaxed)
+# define ATOMIC_DEC_RELAXED(var) atomic_fetch_sub_explicit(&(var), 1, memory_order_relaxed)
+# define ATOMIC_SUB_RELAXED(var, x) atomic_fetch_sub_explicit(&(var), x, memory_order_relaxed)
+#else
+# include <stdint.h>
+
+# define ATOMIC_T uint32_t
+# define ATOMIC_T_MAX UINT32_MAX
+
+# define ATOMIC_STORE_RELAXED(var, x) ((var) = (x))
+# define ATOMIC_LOAD_RELAXED(var) (var)
+# ifndef _WIN32
+# define ATOMIC_INC_RELAXED(var) __sync_fetch_and_add(&(var), 1)
+# define ATOMIC_ADD_RELAXED(var, x) __sync_fetch_and_add(&(var), x)
+# define ATOMIC_DEC_RELAXED(var) __sync_fetch_and_sub(&(var), 1)
+# define ATOMIC_SUB_RELAXED(var, x) __sync_fetch_and_sub(&(var), x)
+# else
+# include <windows.h>
+# define ATOMIC_INC_RELAXED(var) InterlockedExchangeAdd(&(var), 1)
+# define ATOMIC_ADD_RELAXED(var, x) InterlockedExchangeAdd(&(var), x)
+# define ATOMIC_DEC_RELAXED(var) InterlockedExchangeAdd(&(var), -1)
+# define ATOMIC_SUB_RELAXED(var, x) InterlockedExchangeAdd(&(var), -(x))
+# endif
+#endif
+
+#ifndef HAVE_VDPRINTF
+int vdprintf(int fd, const char *format, va_list ap);
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **strp, const char *fmt, ...);
+#endif
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **strp, const char *fmt, va_list ap);
+#endif
+
+#ifndef HAVE_GETLINE
+ssize_t getline(char **lineptr, size_t *n, FILE *stream);
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRNSTR
+char *strnstr(const char *s, const char *find, size_t slen);
+#endif
+
+#ifndef HAVE_STRDUPA
+#define strdupa(s) ( \
+{ \
+ char *buf; \
+ size_t len = strlen(s); \
+ buf = alloca(len + 1); \
+ buf[len] = '\0'; \
+ (char *)memcpy(buf, s, len); \
+})
+#endif
+
+#ifndef HAVE_STRCHRNUL
+char *strchrnul(const char *s, int c);
+#endif
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+char *get_current_dir_name(void);
+#endif
+
+#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
+int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime);
+#endif
+
+#ifndef HAVE_REALPATH
+char *realpath(const char *path, char *resolved_path);
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r(const time_t *timep, struct tm *result);
+#endif
+
+#ifndef HAVE_STRPTIME
+char *strptime(const char *s, const char *format, struct tm *tm);
+#endif
+
+#if defined (_WIN32)
+# define strtok_r strtok_s
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite);
+#endif
+
+#endif /* _COMPAT_H_ */
diff --git a/compat/posix-shims/libgen.h b/compat/posix-shims/libgen.h
new file mode 100644
index 0000000..014bb07
--- /dev/null
+++ b/compat/posix-shims/libgen.h
@@ -0,0 +1 @@
+char *dirname(char *path);
diff --git a/compat/posix-shims/strings.h b/compat/posix-shims/strings.h
new file mode 100644
index 0000000..c917a66
--- /dev/null
+++ b/compat/posix-shims/strings.h
@@ -0,0 +1,9 @@
+#include <compat.h>
+
+#ifndef HAVE_STRCASECMP
+#ifdef _MSC_VER
+#define strcasecmp _stricmp
+#else
+#error No strcasecmp() implementation for this platform is available.
+#endif
+#endif
diff --git a/compat/posix-shims/unistd.h b/compat/posix-shims/unistd.h
new file mode 100644
index 0000000..d7679c5
--- /dev/null
+++ b/compat/posix-shims/unistd.h
@@ -0,0 +1,78 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H 1
+
+/* headers are broken on Windows, which means that some of them simply *have* to come first */
+# include <winsock2.h>
+# include <ws2tcpip.h>
+
+/* This is intended as a drop-in replacement for unistd.h on Windows.
+ * Please add functionality as neeeded.
+ * https://stackoverflow.com/a/826027/1202830
+ */
+
+#include <stdlib.h>
+#include <io.h>
+#include <process.h> /* for getpid() and the exec..() family */
+#include <direct.h> /* for _getcwd() and _chdir() */
+
+#define srandom srand
+#define random rand
+
+/* Values for the second argument to access.
+ These may be OR'd together. */
+#define R_OK 4 /* Test for read permission. */
+#define W_OK 2 /* Test for write permission. */
+#define X_OK 0 /* jkt: unsupported on Windows, so we don't really care */
+#define F_OK 0 /* Test for existence. */
+
+#define access _access
+#define dup2 _dup2
+#define execve _execve
+#define ftruncate _chsize
+#define unlink _unlink
+#define fileno _fileno
+#define getcwd _getcwd
+#define chdir _chdir
+#define isatty _isatty
+#define lseek _lseek
+#define fsync _commit
+#define timegm _mkgmtime
+/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */
+
+#define ssize_t SSIZE_T
+
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+/* should be in some equivalent to <sys/types.h> */
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+#include <windows.h>
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 256
+#endif
+
+#ifndef S_ISREG
+# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#endif
+#ifndef S_IRUSR
+# define S_IRUSR _S_IREAD
+#endif
+#ifndef S_IWUSR
+# define S_IWUSR _S_IWRITE
+#endif
+
+#endif /* unistd.h */
diff --git a/compat/strptime.c b/compat/strptime.c
new file mode 100644
index 0000000..4044fc9
--- /dev/null
+++ b/compat/strptime.c
@@ -0,0 +1,214 @@
+/*
+ * This comes from the musl C library which has been licensed under the permissive MIT license.
+ *
+ * Downloaded from https://git.musl-libc.org/cgit/musl/plain/src/time/strptime.c,
+ * commit 98e688a9da5e7b2925dda17a2d6820dddf1fb287.
+ *
+ * Lobotomized to remove references to nl_langinfo().
+ * Adjusted coding style to fit libyang's uncrustify rules.
+ * */
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+char *
+strptime(const char * restrict s, const char * restrict f, struct tm * restrict tm)
+{
+ int i, w, neg, adj, min, range, *dest, dummy;
+ int want_century = 0, century = 0, relyear = 0;
+
+ while (*f) {
+ if (*f != '%') {
+ if (isspace(*f)) {
+ for ( ; *s && isspace(*s); s++) {}
+ } else if (*s != *f) {
+ return 0;
+ } else {
+ s++;
+ }
+ f++;
+ continue;
+ }
+ f++;
+ if (*f == '+') {
+ f++;
+ }
+ if (isdigit(*f)) {
+ char *new_f;
+
+ w = strtoul(f, &new_f, 10);
+ f = new_f;
+ } else {
+ w = -1;
+ }
+ adj = 0;
+ switch (*f++) {
+ case 'a':
+ case 'A':
+ case 'b':
+ case 'B':
+ case 'h':
+ case 'c':
+ goto fail_nl_langinfo;
+ case 'C':
+ dest = &century;
+ if (w < 0) {
+ w = 2;
+ }
+ want_century |= 2;
+ goto numeric_digits;
+ case 'd':
+ case 'e':
+ dest = &tm->tm_mday;
+ min = 1;
+ range = 31;
+ goto numeric_range;
+ case 'D':
+ s = strptime(s, "%m/%d/%y", tm);
+ if (!s) {
+ return 0;
+ }
+ break;
+ case 'H':
+ dest = &tm->tm_hour;
+ min = 0;
+ range = 24;
+ goto numeric_range;
+ case 'I':
+ dest = &tm->tm_hour;
+ min = 1;
+ range = 12;
+ goto numeric_range;
+ case 'j':
+ dest = &tm->tm_yday;
+ min = 1;
+ range = 366;
+ adj = 1;
+ goto numeric_range;
+ case 'm':
+ dest = &tm->tm_mon;
+ min = 1;
+ range = 12;
+ adj = 1;
+ goto numeric_range;
+ case 'M':
+ dest = &tm->tm_min;
+ min = 0;
+ range = 60;
+ goto numeric_range;
+ case 'n':
+ case 't':
+ for ( ; *s && isspace(*s); s++) {}
+ break;
+ case 'p':
+ case 'r':
+ goto fail_nl_langinfo;
+ case 'R':
+ s = strptime(s, "%H:%M", tm);
+ if (!s) {
+ return 0;
+ }
+ break;
+ case 'S':
+ dest = &tm->tm_sec;
+ min = 0;
+ range = 61;
+ goto numeric_range;
+ case 'T':
+ s = strptime(s, "%H:%M:%S", tm);
+ if (!s) {
+ return 0;
+ }
+ break;
+ case 'U':
+ case 'W':
+ /* Throw away result, for now. (FIXME?) */
+ dest = &dummy;
+ min = 0;
+ range = 54;
+ goto numeric_range;
+ case 'w':
+ dest = &tm->tm_wday;
+ min = 0;
+ range = 7;
+ goto numeric_range;
+ case 'x':
+ case 'X':
+ goto fail_nl_langinfo;
+ case 'y':
+ dest = &relyear;
+ w = 2;
+ want_century |= 1;
+ goto numeric_digits;
+ case 'Y':
+ dest = &tm->tm_year;
+ if (w < 0) {
+ w = 4;
+ }
+ adj = 1900;
+ want_century = 0;
+ goto numeric_digits;
+ case '%':
+ if (*s++ != '%') {
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+numeric_range:
+ if (!isdigit(*s)) {
+ return 0;
+ }
+ *dest = 0;
+ for (i = 1; i <= min + range && isdigit(*s); i *= 10) {
+ *dest = *dest * 10 + *s++ - '0';
+ }
+ if (*dest - min >= range) {
+ return 0;
+ }
+ *dest -= adj;
+ switch ((char *)dest - (char *)tm) {
+ case offsetof(struct tm, tm_yday):
+ ;
+ }
+ goto update;
+numeric_digits:
+ neg = 0;
+ if (*s == '+') {
+ s++;
+ } else if (*s == '-') {
+ neg = 1, s++;
+ }
+ if (!isdigit(*s)) {
+ return 0;
+ }
+ for (*dest = i = 0; i < w && isdigit(*s); i++) {
+ *dest = *dest * 10 + *s++ - '0';
+ }
+ if (neg) {
+ *dest = -*dest;
+ }
+ *dest -= adj;
+ goto update;
+update:
+ // FIXME
+ ;
+ }
+ }
+ if (want_century) {
+ tm->tm_year = relyear;
+ if (want_century & 2) {
+ tm->tm_year += century * 100 - 1900;
+ } else if (tm->tm_year <= 68) {
+ tm->tm_year += 100;
+ }
+ }
+ return (char *)s;
+fail_nl_langinfo:
+ fprintf(stderr, "strptime: nl_langinfo not available");
+ return NULL;
+}