text: /*
   * 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>
   */
  
  #include "config.h"
  
  /*
   * This is needed to expose pthread_mutexattr_settype and PTHREAD_MUTEX_DEFAULT
   * on older pthreads implementations
   *
   * This is excluded on SunOS due to it making the code compile in < XPG6 mode
   * which is not supported with a C99 compiler.
   */
  #ifndef __sun
  #define _XOPEN_SOURCE 700
  #endif
  
  /*
   * This is needed to expose issetugid, getresuid, and getresgid, which are
   * hidden with the _XOPEN_SOURCE setting above
   */
  #ifdef __FreeBSD__
  #undef __BSD_VISIBLE
  #define __BSD_VISIBLE 1
  #endif
  
  #include "compat.h"
  #include "debug.h"
  
  #include <assert.h>
  #include <dirent.h>
  #include <errno.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <string.h>
  #ifdef OS_UNIX
  #include <unistd.h>
  #endif
  
  /*-
   * Portions of this file are covered by the following copyright:
   *
   * Copyright (c) 2001 Mike Barcroft <mike@FreeBSD.org>
   * Copyright (c) 1990, 1993
   * Copyright (c) 1987, 1993
   *      The Regents of the University of California.  All rights reserved.
   *
   * This code is derived from software contributed to Berkeley by
   * Chris Torek.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. 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.
   * 4. Neither the name of the University nor the names of its contributors
   *    may be used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
   */
  
  #ifndef HAVE_GETPROGNAME
  
  #ifdef OS_UNIX
  
  #if defined (HAVE_PROGRAM_INVOCATION_SHORT_NAME) && !HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME
  extern char *program_invocation_short_name;
  #endif
  
  #if defined (HAVE___PROGNAME) && !HAVE_DECL___PROGNAME
  extern char *__progname;
  #endif
  
  #ifdef __linux__
  /* This symbol is also defined in library.c so as to be freed by the library
   * destructor.  If weak symbols are not supported nor library.c is not linked we
   * simply leak the memory allocated with realpath().  */
  #ifdef __GNUC__
  extern char *p11_program_realpath;
  
  char *p11_program_realpath __attribute__((weak));
  #else
  static char *p11_program_realpath;
  #endif
  #endif
  
  const char *
  getprogname (void)
  {
  	const char *name;
  
  #if defined (HAVE_GETEXECNAME)
  	const char *p;
  	name = getexecname();
  	p = strrchr (name ? name : "", '/');
  	if (p != NULL)
  		name = p + 1;
  #elif defined (HAVE_PROGRAM_INVOCATION_SHORT_NAME)
  #ifdef __linux__
  	name = program_invocation_name;
  	assert (name);
  	if (*name == '/') {
  		/*
  		 * Some programs pack command line arguments into argv[0].
  		 * Check if it is the case by reading /proc/self/exe and extract
  		 * the program name.
  		 *
  		 * Logic borrowed from:
  		 * <https://github.com/mesa3d/mesa/commit/759b94038987bb983398cd4b1d2cb1c8f79817a9>.
  		 */
  		if (!p11_program_realpath)
  			p11_program_realpath = realpath ("/proc/self/exe", NULL);
  
  		if (p11_program_realpath &&
  		    strncmp (p11_program_realpath, name,
  			     strlen (p11_program_realpath)) == 0)
  			/* Use the executable path if the prefix matches. */
  			name = strrchr (p11_program_realpath, '/') + 1;
  		else
  			/* Otherwise fall back to
  			 * program_invocation_short_name. */
  			name = program_invocation_short_name;
  	} else {
  		name = program_invocation_short_name;
  	}
  #else
  	name = program_invocation_short_name;
  #endif
  #elif defined (HAVE___PROGNAME)
  	name = __progname;
  #else
  	#error No way to retrieve short program name
  #endif
  
  	return name;
  }
  
  #else /* OS_WIN32 */
  
  extern char **__argv;
  static char prognamebuf[256];
  
  const char *
  getprogname (void)
  {
  	const char *name;
  	const char *p, *p2;
  	size_t length;
  
  	name = __argv[0];
  	if (name == NULL)
  		return NULL;
  
  	p = strrchr (name, '\\');
  	p2 = strrchr (name, '/');
  	if (p2 > p)
  		p = p2;
  	if (p != NULL)
  		name = p + 1;
  
  	length = sizeof (prognamebuf) - 1;
  	strncpy (prognamebuf, name, length);
  	prognamebuf[length] = 0;
  	length = strlen (prognamebuf);
  	if (length > 4 && _stricmp (prognamebuf + (length - 4), ".exe") == 0)
  		prognamebuf[length - 4] = '\0';
  
  	return prognamebuf;
  }
  
  #endif /* OS_WIN32 */
  
  #endif /* HAVE_GETPROGNAME */
  
  #ifdef OS_UNIX
  #include <sys/stat.h>
  #include <sys/mman.h>
  #include <fcntl.h>
  #include <unistd.h>
  
  #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
  void
  p11_recursive_mutex_init (p11_mutex_t *mutex)
  {
  	pthread_mutexattr_t attr;
  	int ret;
  
  	pthread_mutexattr_init (&attr);
  	pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
  	ret = pthread_mutex_init (mutex, &attr);
  	assert (ret == 0);
  	pthread_mutexattr_destroy (&attr);
  }
  #endif
  
  char *
  p11_dl_error (void)
  {
  	const char *msg = dlerror ();
  	return msg ? strdup (msg) : NULL;
  }
  
  struct _p11_mmap {
  	int fd;
  	void *data;
  	size_t size;
  };
  
  p11_mmap *
  p11_mmap_open (const char *path,
                 struct stat *sb,
                 void **data,
                 size_t *size)
  {
  	struct stat stb;
  	p11_mmap *map;
  
  	map = calloc (1, sizeof (p11_mmap));
  	if (map == NULL)
  		return NULL;
  
  	map->fd = open (path, O_RDONLY | O_CLOEXEC);
  	if (map->fd == -1) {
  		free (map);
  		return NULL;
  	}
  
  	if (sb == NULL) {
  		sb = &stb;
  		if (fstat (map->fd, &stb) < 0) {
  			close (map->fd);
  			free (map);
  			return NULL;
  		}
  	}
  
  	/* Workaround for broken ZFS on Linux */
  	if (S_ISDIR (sb->st_mode)) {
  		errno = EISDIR;
  		close (map->fd);
  		free (map);
  		return NULL;
  	}
  
  	if (sb->st_size == 0) {
  		*data = "";
  		*size = 0;
  		return map;
  	}
  
  	map->size = sb->st_size;
  	map->data = mmap (NULL, map->size, PROT_READ, MAP_PRIVATE, map->fd, 0);
  	if (map->data == MAP_FAILED) {
  		close (map->fd);
  		free (map);
  		return NULL;
  	}
  
  	*data = map->data;
  	*size = map->size;
  	return map;
  }
  
  void
  p11_mmap_close (p11_mmap *map)
  {
  	if (map->size)
  		munmap (map->data, map->size);
  	close (map->fd);
  	free (map);
  }
  
  #endif /* OS_UNIX */
  
  #ifdef OS_WIN32
  
  char *
  p11_dl_error (void)
  {
  	DWORD code = GetLastError();
  	LPVOID msg_buf;
  	char *result;
  
  	FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER |
  	                FORMAT_MESSAGE_FROM_SYSTEM |
  	                FORMAT_MESSAGE_IGNORE_INSERTS,
  	                NULL, code,
  	                MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  	                (LPSTR)&msg_buf, 0, NULL);
  
  	result = strdup (msg_buf);
  	LocalFree (msg_buf);
  	return result;
  }
  
  int
  p11_thread_create (p11_thread_t *thread,
                     p11_thread_routine routine,
                     void *arg)
  {
  	assert (thread);
  
  	*thread = CreateThread (NULL, 0,
  	                        (LPTHREAD_START_ROUTINE)routine,
  	                        arg, 0, NULL);
  
  	if (*thread == NULL)
  		return GetLastError ();
  
  	return 0;
  }
  
  int
  p11_thread_join (p11_thread_t thread)
  {
  	DWORD res;
  
  	res = WaitForSingleObject (thread, INFINITE);
  	if (res == WAIT_FAILED)
  		return GetLastError ();
  
  	CloseHandle (thread);
  	return 0;
  }
  
  struct _p11_mmap {
  	HANDLE file;
  	HANDLE mapping;
  	void *data;
  };
  
  p11_mmap *
  p11_mmap_open (const char *path,
                 struct stat *sb,
                 void **data,
                 size_t *size)
  {
  	HANDLE mapping;
  	LARGE_INTEGER large;
  	DWORD errn;
  	p11_mmap *map;
  
  	map = calloc (1, sizeof (p11_mmap));
  	if (map == NULL) {
  		errno = ENOMEM;
  		return NULL;
  	}
  
  	map->file  = CreateFile (path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
  	if (map->file == INVALID_HANDLE_VALUE) {
  		errn = GetLastError ();
  		free (map);
  		SetLastError (errn);
  		if (errn == ERROR_PATH_NOT_FOUND || errn == ERROR_FILE_NOT_FOUND)
  			errno = ENOENT;
  		else if (errn == ERROR_ACCESS_DENIED)
  			errno = EPERM;
  		return NULL;
  	}
  
  	if (sb == NULL) {
  		if (!GetFileSizeEx (map->file, &large)) {
  			errn = GetLastError ();
  			CloseHandle (map->file);
  			free (map);
  			SetLastError (errn);
  			if (errn == ERROR_ACCESS_DENIED)
  				errno = EPERM;
  			return NULL;
  		}
  	} else {
  		large.QuadPart = sb->st_size;
  	}
  
  	mapping = CreateFileMapping (map->file, NULL, PAGE_READONLY, 0, 0, NULL);
  	if (!mapping) {
  		errn = GetLastError ();
  		CloseHandle (map->file);
  		free (map);
  		SetLastError (errn);
  		if (errn == ERROR_ACCESS_DENIED)
  			errno = EPERM;
  		return NULL;
  	}
  
  	map->data = MapViewOfFile (mapping, FILE_MAP_READ, 0, 0, large.QuadPart);
  	CloseHandle (mapping);
  
  	if (map->data == NULL) {
  		errn = GetLastError ();
  		CloseHandle (map->file);
  		free (map);
  		SetLastError (errn);
  		if (errn == ERROR_ACCESS_DENIED)
  			errno = EPERM;
  		return NULL;
  	}
  
  	*data = map->data;
  	*size = large.QuadPart;
  	return map;
  }
  
  void
  p11_mmap_close (p11_mmap *map)
  {
  	UnmapViewOfFile (map->data);
  	CloseHandle (map->file);
  	free (map);
  }
  
  #endif /* OS_WIN32 */
  
  #ifndef HAVE_STRNSTR
  #include <string.h>
  
  /*
   * Find the first occurrence of find in s, where the search is limited to the
   * first slen characters of s.
   */
  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) != 0);
  		s--;
  	}
  	return ((char *)s);
  }
  
  #endif /* HAVE_STRNSTR */
  
  #ifndef HAVE_MEMDUP
  
  void *
  memdup (const void *data,
          size_t length)
  {
  	void *dup;
  
  	if (!data)
  		return NULL;
  
  	dup = malloc (length);
  	if (dup != NULL)
  		memcpy (dup, data, length);
  
  	return dup;
  }
  
  #endif /* HAVE_MEMDUP */
  
  /*
   * WORKAROUND: So in lots of released builds of firefox a completely broken strndup()
   * is present. It does not NULL terminate its string output. It is unconditionally
   * defined, and overrides the libc strndup() function on platforms where it
   * exists as a function. For this reason we (for now) unconditionally define
   * strndup().
   */
  
  #if 1 /* #ifndef HAVE_STRNDUP */
  
  /*
   * HAVE_STRNDUP may be undefined if strndup() isn't working. So it may be
   * present, and yet strndup may still be a defined header macro.
   */
  #ifdef strndup
  #undef strndup
  #endif
  
  char *
  strndup (const char *data,
           size_t length);
  
  char *
  strndup (const char *data,
           size_t length)
  {
  	char *ret;
  
  	ret = malloc (length + 1);
  	if (ret != NULL) {
  		strncpy (ret, data, length);
  		ret[length] = 0;
  	}
  
  	return ret;
  }
  
  #endif /* HAVE_STRNDUP */
  
  #ifndef HAVE_REALLOCARRAY
  
  void *
  reallocarray (void *ptr,
  	      size_t nmemb,
  	      size_t size)
  {
  	assert (nmemb >= 0 && size >= 0);
  	if (nmemb != 0 && SIZE_MAX / nmemb < size) {
  		errno = ENOMEM;
  		return NULL;
  	}
  	return realloc (ptr, nmemb * size);
  }
  
  #endif /* HAVE_MEMDUP */
  
  #ifndef HAVE_STRCONCAT
  
  #include <stdarg.h>
  
  char *
  strconcat (const char *first,
             ...)
  {
  	size_t length = 0;
  	const char *arg;
  	char *result, *at;
  	va_list va;
  
  	va_start (va, first);
  
  	for (arg = first; arg; arg = va_arg (va, const char*)) {
  		size_t old_length = length;
  		length += strlen (arg);
  		if (length < old_length) {
  			va_end (va);
  			return_val_if_reached (NULL);
  		}
  	}
  
  	va_end (va);
  
  	at = result = malloc (length + 1);
  	if (result == NULL)
  	       return NULL;
  
  	va_start (va, first);
  
  	for (arg = first; arg; arg = va_arg (va, const char*)) {
  	       length = strlen (arg);
  	       memcpy (at, arg, length);
  	       at += length;
  	}
  
  	va_end (va);
  
  	*at = 0;
  	return result;
  }
  
  #endif /* HAVE_STRCONCAT */
  
  #ifndef HAVE_VASPRINTF
  #include <stdio.h>
  
  int vasprintf(char **strp, const char *fmt, va_list ap);
  
  int
  vasprintf (char **strp,
             const char *fmt,
             va_list ap)
  {
  	char *buf = NULL;
  	char *nbuf;
  	int guess = 128;
  	int length = 0;
  	va_list orig, aq;
  	int ret;
  
  	if (fmt == NULL) {
  		errno = EINVAL;
  		return -1;
  	}
  
  	va_copy (orig, ap);
  	for (;;) {
  		nbuf = realloc (buf, guess);
  		if (!nbuf) {
  			free (buf);
  			va_end (orig);
  			return -1;
  		}
  
  		buf = nbuf;
  		length = guess;
  
  		va_copy (aq, orig);
  		ret = vsnprintf (buf, length, fmt, aq);
  		va_end (aq);
  
  		if (ret < 0)
  			guess *= 2;
  
  		else if (ret >= length)
  			guess = ret + 1;
  
  		else
  			break;
  	}
  	va_end (orig);
  
  	*strp = buf;
  	return ret;
  }
  
  #endif /* HAVE_VASPRINTF */
  
  #ifndef HAVE_ASPRINTF
  
  int asprintf(char **strp, const char *fmt, ...);
  
  int
  asprintf (char **strp,
            const char *fmt,
            ...)
  {
  	va_list va;
  	int ret;
  
  	va_start (va, fmt);
  	ret = vasprintf (strp, fmt, va);
  	va_end (va);
  
  	return ret;
  }
  
  #endif /* HAVE_ASPRINTF */
  
  #ifndef HAVE_GMTIME_R
  
  struct tm *
  gmtime_r (const time_t *timep,
            struct tm *result)
  {
  #ifdef OS_WIN32
  	/*
  	 * On win32 gmtime() returns thread local storage, so we can
  	 * just copy it out into the buffer without worrying about races.
  	 */
  	struct tm *tg;
  	tg = gmtime (timep);
  	if (!tg)
  		return NULL;
  	memcpy (result, tg, sizeof (struct tm));
  	return result;
  #else
  	#error Need either gmtime_r() function on Unix
  #endif
  }
  
  #endif /* HAVE_GMTIME_R */
  
  #if !defined(HAVE_MKDTEMP) || !defined(HAVE_MKSTEMP)
  #include <sys/stat.h>
  #include <fcntl.h>
  
  static int
  _gettemp (char *path,
            int *doopen,
            int domkdir,
            int slen)
  {
  	static const char padchar[] =
  		"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  	static const int maxpathlen = 1024;
  
  	char *start, *trv, *suffp, *carryp;
  	char *pad;
  	struct stat sbuf;
  	int rval;
  	int rnd;
  	char carrybuf[maxpathlen];
  
  	if ((doopen != NULL && domkdir) || slen < 0) {
  		errno = EINVAL;
  		return (0);
  	}
  
  	for (trv = path; *trv != '\0'; ++trv)
  		;
  	if (trv - path >= maxpathlen) {
  		errno = ENAMETOOLONG;
  		return (0);
  	}
  	trv -= slen;
  	suffp = trv;
  	--trv;
  	if (trv < path || NULL != strchr (suffp, '/')) {
  		errno = EINVAL;
  		return (0);
  	}
  
  	/* Fill space with random characters */
  	while (trv >= path && *trv == 'X') {
  		rnd = rand () % (sizeof (padchar) - 1);
  		*trv-- = padchar[rnd];
  	}
  	start = trv + 1;
  
  	/* save first combination of random characters */
  	memcpy (carrybuf, start, suffp - start);
  
  	/*
  	 * check the target directory.
  	 */
  	if (doopen != NULL || domkdir) {
  		for (; trv > path; --trv) {
  			if (*trv == '/') {
  				*trv = '\0';
  				rval = stat(path, &sbuf);
  				*trv = '/';
  				if (rval != 0)
  					return (0);
  				if (!S_ISDIR(sbuf.st_mode)) {
  					errno = ENOTDIR;
  					return (0);
  				}
  				break;
  			}
  		}
  	}
  
  	for (;;) {
  		if (doopen) {
  			if ((*doopen = open (path, O_BINARY | O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600)) >= 0)
  				return (1);
  			if (errno != EEXIST)
  				return (0);
  		} else if (domkdir) {
  #ifdef OS_UNIX
  			if (mkdir (path, 0700) == 0)
  #else
  			if (mkdir (path) == 0)
  #endif
  				return (1);
  			if (errno != EEXIST)
  				return (0);
  #ifdef OS_UNIX
  		} else if (lstat (path, &sbuf))
  #else
  		} else if (stat (path, &sbuf))
  #endif
  			return (errno == ENOENT);
  
  		/* If we have a collision, cycle through the space of filenames */
  		for (trv = start, carryp = carrybuf;;) {
  			/* have we tried all possible permutations? */
  			if (trv == suffp)
  				return (0); /* yes - exit with EEXIST */
  			pad = strchr(padchar, *trv);
  			if (pad == NULL) {
  				/* this should never happen */
  				errno = EIO;
  				return (0);
  			}
  			/* increment character */
  			*trv = (*++pad == '\0') ? padchar[0] : *pad;
  			/* carry to next position? */
  			if (*trv == *carryp) {
  				/* increment position and loop */
  				++trv;
  				++carryp;
  			} else {
  				/* try with new name */
  				break;
  			}
  		}
  	}
  
  	/*NOTREACHED*/
  }
  
  #endif /* !HAVE_MKDTEMP || !HAVE_MKSTEMP */
  
  #ifndef HAVE_MKSTEMP
  
  int
  mkstemp (char *template)
  {
  	int fd;
  
  	return (_gettemp (template, &fd, 0, 0) ? fd : -1);
  }
  
  #endif /* HAVE_MKSTEMP */
  
  #ifndef HAVE_MKDTEMP
  
  char *
  mkdtemp (char *template)
  {
  	return (_gettemp (template, (int *)NULL, 1, 0) ? template : (char *)NULL);
  }
  
  #endif /* HAVE_MKDTEMP */
  
  #ifndef HAVE_GETAUXVAL
  
  unsigned long
  getauxval (unsigned long type)
  {
  	static unsigned long secure = 0UL;
  	static bool check_secure_initialized = false;
  
  	/*
  	 * This is the only one our stand-in impl supports and is
  	 * also the only type we define in compat.h header
  	 */
  	assert (type == AT_SECURE);
  
  	if (!check_secure_initialized) {
  #if defined(HAVE___LIBC_ENABLE_SECURE)
  		extern int __libc_enable_secure;
  		secure = __libc_enable_secure;
  
  #elif defined(HAVE_ISSETUGID) && \
  	!((defined __APPLE__ && defined __MACH__) || (defined __FreeBSD__))
  		secure = issetugid ();
  
  #elif defined(OS_UNIX)
  		uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
  		gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
  
  #ifdef HAVE_GETRESUID
  		if (getresuid (&ruid, &euid, &suid) != 0 ||
  		    getresgid (&rgid, &egid, &sgid) != 0)
  #endif /* HAVE_GETRESUID */
  		{
  			suid = ruid = getuid ();
  			sgid = rgid = getgid ();
  			euid = geteuid ();
  			egid = getegid ();
  		}
  
  		secure = (ruid != euid || ruid != suid ||
  		          rgid != egid || rgid != sgid);
  #endif /* OS_UNIX */
  		check_secure_initialized = true;
  	}
  
  	return secure;
  }
  
  #endif /* HAVE_GETAUXVAL */
  
  char *
  secure_getenv (const char *name)
  {
  	if (getauxval (AT_SECURE))
  		return NULL;
  	return getenv (name);
  }
  
  #ifndef HAVE_STRERROR_R
  
  int
  strerror_r (int errnum,
              char *buf,
              size_t buflen)
  {
  #ifdef OS_WIN32
  #if _WIN32_WINNT < 0x502 /* WinXP or older */
  	int n = sys_nerr;
  	const char *p;
  	if (errnum < 0 || errnum >= n)
  		p = sys_errlist[n];
  	else
  		p = sys_errlist[errnum];
  	if (buf == NULL || buflen == 0)
  		return EINVAL;
  	strncpy(buf, p, buflen);
  	buf[buflen-1] = 0;
  	return 0;
  #else /* Server 2003 or newer */
  	return strerror_s (buf, buflen, errnum);
  #endif /*_WIN32_WINNT*/
  
  #else
  	#error no strerror_r implementation
  #endif
  }
  
  #endif /* HAVE_STRERROR_R */
  
  void
  p11_dl_close (void *dl)
  {
  #ifdef OS_WIN32
  	FreeLibrary (dl);
  #else
  	(void) dlclose (dl);
  #endif
  }
  
  
  #ifdef OS_UNIX
  
  #include <unistd.h>
  
  #ifndef HAVE_FDWALK
  
  #ifdef HAVE_SYS_RESOURCE_H
  #include <sys/resource.h>
  #endif
  
  int
  fdwalk (int (* cb) (void *data, int fd),
          void *data)
  {
  	int open_max;
  	int res = 0;
  	int fd;
  
  #ifdef HAVE_SYS_RESOURCE_H
  	struct rlimit rl;
  #endif
  
  #ifdef __linux__
  	DIR *dir;
  
  	dir = opendir ("/proc/self/fd");
  	if (dir != NULL) {
  		struct dirent *de;
  
  		while ((de = readdir (dir)) != NULL) {
  			char *end;
  			long num;
  
  			end = NULL;
  			num = (int) strtol (de->d_name, &end, 10);
  
  			/* didn't parse or is the opendir() fd */
  			if (!end || *end != '\0' ||
  			    (int)num == dirfd (dir))
  				continue;
  
  			fd = num;
  
  			/* call the callback */
  			res = cb (data, fd);
  			if (res != 0)
  				break;
  		}
  
  		closedir (dir);
  		return res;
  	}
  #endif
  
  	/* No /proc, brute force */
  #ifdef HAVE_SYS_RESOURCE_H
  	if (getrlimit (RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
  		open_max = rl.rlim_max;
  	else
  #endif
  		open_max = sysconf (_SC_OPEN_MAX);
  
  	for (fd = 0; fd < open_max; fd++) {
  		res = cb (data, fd);
  		if (res != 0)
  			break;
  	}
  
  	return res;
  }
  
  #endif /* HAVE_FDWALK */
  
  #endif /* OS_UNIX */
  
  int
  p11_ascii_tolower (int c)
  {
  	if (c >= 'A' && c <= 'Z')
  		return 'a' + (c - 'A');
  	return c;
  }
  
  int
  p11_ascii_toupper (int c)
  {
  	if (c >= 'a' && c <= 'z')
  		return 'A' + (c - 'a');
  	return c;
  }
  