/*
 * Copyright (c) 2011 Collabora Ltd.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above
 *       copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the
 *       above copyright notice, this list of conditions and
 *       the following disclaimer in the documentation and/or
 *       other materials provided with the distribution.
 *     * The names of contributors to this software may not be
 *       used to endorse or promote products derived from this
 *       software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * Author: Stef Walter <stefw@collabora.co.uk>
 */

#ifndef __COMPAT_H__
#define __COMPAT_H__

#include "config.h"

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef HAVE_SYS_UN_H
#  include <sys/un.h>
#endif

#if !defined(__cplusplus) && (__GNUC__ > 2)
#define GNUC_PRINTF(x, y) __attribute__((__format__(__printf__, x, y)))
#else
#define GNUC_PRINTF(x, y)
#endif

#if __GNUC__ >= 4
#define GNUC_NULL_TERMINATED __attribute__((__sentinel__))
#else
#define GNUC_NULL_TERMINATED
#endif

#if ((defined __STDC_VERSION__ && 201112L <= __STDC_VERSION__) \
     || (!defined __STRICT_ANSI__ && 4 < __GNUC__ + (6 <= __GNUC_MINOR__)))
# define P11_STATIC_ASSERT(x) _Static_assert(x, "static assertion failed")
#else
# define P11_STATIC_ASSERT(x)
#endif

/* For detecting clang features */
#ifndef __has_feature
#define __has_feature(x) 0
#endif

#ifndef CLANG_ANALYZER_NORETURN
#if defined(__cppcheck__)
#define CLANG_ANALYZER_NORETURN __attribute__((__noreturn__))
#elif __has_feature(attribute_analyzer_noreturn)
#define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
#else
#define CLANG_ANALYZER_NORETURN
#endif
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif

#ifndef HAVE_GETPROGNAME
const char * getprogname (void);
#endif

#ifndef HAVE_MKSTEMP

int          mkstemp     (char *template);

#endif /* HAVE_MKSTEMP */

#ifndef HAVE_MKDTEMP

char *       mkdtemp     (char *template);

#endif /* HAVE_MKDTEMP */

char *       strdup_path_mangle (const char *template);

void         p11_dl_close       (void * dl);

/* -----------------------------------------------------------------------------
 * WIN32
 */

#ifdef OS_WIN32

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x500
#endif

#ifndef _WIN32_IE
#define _WIN32_IE 0x500
#endif

#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>

#include <io.h>

/* Oh ... my ... god */
#undef CreateMutex

typedef CRITICAL_SECTION p11_mutex_t;

typedef HANDLE p11_thread_t;

typedef DWORD p11_thread_id_t;

#define P11_RECURSIVE_MUTEX_INIT(m) \
	(InitializeCriticalSection (&m))
#define p11_mutex_init(m) \
	(InitializeCriticalSection (m))
#define p11_mutex_lock(m) \
	(EnterCriticalSection (m))
#define p11_mutex_unlock(m) \
	(LeaveCriticalSection (m))
#define p11_mutex_uninit(m) \
	(DeleteCriticalSection (m))

typedef void * (*p11_thread_routine) (void *arg);

int p11_thread_create (p11_thread_t *thread, p11_thread_routine, void *arg);

int p11_thread_join (p11_thread_t thread);

/* Returns a thread_id_t */
#define p11_thread_id_self() \
	(GetCurrentThreadId ())

typedef HMODULE dl_module_t;

#define p11_dl_open(f) \
	(LoadLibrary (f))
#define p11_dl_symbol(d, s) \
	((void *)GetProcAddress ((d), (s)))

char *    p11_dl_error       (void);

#define p11_sleep_ms(ms) \
	(Sleep (ms))

typedef struct _p11_mmap p11_mmap;

p11_mmap *  p11_mmap_open   (const char *path,
                             struct stat *sb,
                             void **data,
                             size_t *size);

void        p11_mmap_close  (p11_mmap *map);

#ifndef HAVE_SETENV
#define setenv(n, v, z) _putenv_s(n, v)
#endif /* HAVE_SETENV */

#endif /* OS_WIN32 */

/* ----------------------------------------------------------------------------
 * UNIX
 */

#ifdef OS_UNIX

#include <pthread.h>
#include <dlfcn.h>
#include <time.h>

typedef pthread_mutex_t p11_mutex_t;

#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP

/* No implementation, because done by static initializer */
#define P11_RECURSIVE_MUTEX_INIT(mutex)

#else

#define P11_RECURSIVE_MUTEX_INIT(mutex) \
	(p11_recursive_mutex_init (&(mutex)))

void        p11_recursive_mutex_init          (p11_mutex_t *mutex);

#endif

#define p11_mutex_init(m) \
	(pthread_mutex_init (m, NULL))
#define p11_mutex_lock(m) \
	(pthread_mutex_lock (m))
#define p11_mutex_unlock(m) \
	(pthread_mutex_unlock (m))
#define p11_mutex_uninit(m) \
	(pthread_mutex_destroy(m))

typedef pthread_cond_t p11_cond_t;

#define p11_cond_init(c) \
	(pthread_cond_init (c, NULL))
#define p11_cond_wait(c, m) \
        (pthread_cond_wait (c, m))
#define p11_cond_signal(c) \
        (pthread_cond_signal (c))
#define p11_cond_broadcast(c) \
        (pthread_cond_broadcast (c))
#define p11_cond_uninit(c) \
        (pthread_cond_destroy (c))

typedef pthread_t p11_thread_t;

typedef pthread_t p11_thread_id_t;

typedef void * (*p11_thread_routine) (void *arg);

#define p11_thread_create(t, r, a) \
	(pthread_create ((t), NULL, (r), (a)))
#define p11_thread_join(t) \
	(pthread_join ((t), NULL))
#define p11_thread_id_self(m) \
	(pthread_self ())

typedef void * dl_module_t;

#define p11_dl_open(f) \
	(dlopen ((f), RTLD_LOCAL | RTLD_NOW))
#define p11_dl_symbol(d, s) \
	(dlsym ((d), (s)))

char * p11_dl_error (void);

#define p11_sleep_ms(ms) \
	do { int _ms = (ms); \
	struct timespec _ts = { _ms / 1000, (_ms % 1000) * 1000 * 1000 }; \
	nanosleep (&_ts, NULL); \
	} while(0)

typedef struct _p11_mmap p11_mmap;

p11_mmap *  p11_mmap_open   (const char *path,
                             struct stat *sb,
                             void **data,
                             size_t *size);

void        p11_mmap_close  (p11_mmap *map);

#endif /* OS_UNIX */

/* ----------------------------------------------------------------------------
 * MORE COMPAT
 */

#ifdef	HAVE_ERRNO_H
#include <errno.h>
#endif	/* HAVE_ERRNO_H */

#ifndef HAVE_STRNSTR

char *     strnstr          (const char *s,
                             const char *find,
                             size_t slen);

#endif /* HAVE_STRNSTR */

#ifndef HAVE_MEMDUP

void *     memdup           (const void *data,
                             size_t length);

#endif /* HAVE_MEMDUP */

#ifndef HAVE_STRNDUP

char *     strndup          (const char *data,
                             size_t length);

#endif /* HAVE_STRDUP */

#if defined HAVE_DECL_REALLOCARRAY && !HAVE_DECL_REALLOCARRAY

void *     reallocarray     (void *ptr,
                             size_t nmemb,
                             size_t size);

#endif /* HAVE_REALLOCARRAY */

#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#else
typedef enum { false, true } bool;
#endif

#ifndef HAVE_STRCONCAT

char *     strconcat        (const char *first,
                             ...) GNUC_NULL_TERMINATED;

#endif /* HAVE_STRCONCAT */

#if defined HAVE_DECL_ASPRINTF && !HAVE_DECL_ASPRINTF

int        asprintf         (char **strp,
                             const char *fmt,
                             ...);

#endif /* HAVE_ASPRINTF */

#if defined HAVE_DECL_VASPRINTF && !HAVE_DECL_VASPRINTF
#include <stdarg.h>

int        vasprintf        (char **strp,
                             const char *fmt,
                             va_list ap);

#endif /* HAVE_DECL_VASPRINTF */

#ifndef HAVE_GMTIME_R
#include <time.h>

struct tm * gmtime_r        (const time_t *timep,
                             struct tm *result);

#endif /* HAVE_GMTIME_R */

#ifndef HAVE_TIMEGM
#include <time.h>

time_t      timegm          (struct tm *tm);

#endif /* HAVE_TIMEGM */

#ifdef HAVE_GETAUXVAL

#include <sys/auxv.h>
#define _p11_getauxval(X) getauxval(X)

#else /* !HAVE_GETAUXVAL */

unsigned long     _p11_getauxval (unsigned long type);

#define AT_SECURE 23

#endif /* !HAVE_GETAUXVAL */

char *            secure_getenv (const char *name);

#ifndef HAVE_FDWALK

int        fdwalk           (int (* cb) (void *data, int fd),
                             void *data);

#endif

#ifdef HAVE_ISATTY

#include <unistd.h>

#else

int        isatty           (int fd);

#endif /* HAVE_ISATTY */

#ifdef HAVE_READPASSPHRASE

#include <readpassphrase.h>

#else

#define RPP_ECHO_OFF    0x00		/* Turn off echo (default). */
#define RPP_ECHO_ON     0x01		/* Leave echo on. */
#define RPP_REQUIRE_TTY 0x02		/* Fail if there is no tty. */
#define RPP_FORCELOWER  0x04		/* Force input to lower case. */
#define RPP_FORCEUPPER  0x08		/* Force input to upper case. */
#define RPP_SEVENBIT    0x10		/* Strip the high bit from input. */
#define RPP_STDIN       0x20		/* Read from stdin, not /dev/tty */

char      *readpassphrase   (const char *prompt,
                             char *buf,
                             size_t bufsiz,
                             int flags);

#endif /* HAVE_READPASSPHRASE */

/* If either locale_t or newlocale() is not available, strerror_l()
 * cannot be used */
#if !defined(HAVE_LOCALE_T) || !defined(HAVE_NEWLOCALE)
#undef HAVE_STRERROR_L
#endif

#ifndef SUN_LEN

#define SUN_LEN(sa) (sizeof(struct sockaddr_un) \
	- sizeof((sa)->sun_path) + strlen((sa)->sun_path))

#endif

void       p11_strerror_r      (int errnum,
                                char *buf,
                                size_t buflen);

int        p11_ascii_tolower   (int c);
int        p11_ascii_toupper   (int c);

bool       p11_ascii_strcaseeq (const char *s1,
                                const char *s2);

#endif /* __COMPAT_H__ */
