File master.patch of Package libfaketime

diff --git a/NEWS b/NEWS
index 8836d80..a718198 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,21 @@
+Since 0.9.7:
+    - Passthrough for unknown clock ids to avoid error messages
+    - Fixes for multithreaded operations (mliertzer, qnox)
+    - glibc-related fixes (jprjr) and gcc8 support (tpetazzoni)
+    - Improved error message output on parsing errors
+    - fix file stat() faking when 'i' modifier is used for determinism
+    - Use FAKETIME="%" to take FAKETIME setting from a file as
+      specified in FAKETIME_FOLLOW_FILE
+    - Added FAKETIME_DONT_RESET environment variable to avoid
+      faketime resets when subprocesses are started (similar to
+      the old v0.9.6 behavior)
+    - Do not fake time during libfaketime initialization to
+      improve compatibility with memory allocation libraries that
+      use time-related functions themselves
+    - Fixes for shared memory related issues, especially when
+      not using the faketime wrapper
+    - Updated glibc compatibility settings for various platforms
+
 Since 0.9.6:
     - Julien Gilli added an option to disable monotonic time faking
     - Azat Khuzhin added support for COARSE clocks
diff --git a/README b/README
index 248bc92..8d52e90 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
-libfaketime, version 0.9.7 (November 2017)
-==========================================
+libfaketime, version 0.9.8 (August 2019)
+========================================
 
 
 Content of this file:
@@ -71,8 +71,8 @@ documentation whether it can be achieved by using libfaketime directly.
   rebuilding libfaketimeMT.so.1
 
 * Java-/JVM-based applications work but you need to pass in an extra argument
-  (DONT_FAKE_MONOTONIC).  See usage basics below for details.  Without this
-  argument the java command usually hangs.
+  (FAKETIME_DONT_FAKE_MONOTONIC).  See usage basics below for details.  Without
+  this argument the java command usually hangs.
 
 * libfaketime will eventually be bypassed by applications that dynamically load
   system libraries, such as librt, explicitly themselves instead of relying on
@@ -141,7 +141,7 @@ user@host> LD_PRELOAD=/usr/local/lib/libfaketime.so.1 FAKETIME="-15d" date
 Mon Nov  8 12:01:12 CEST 2016
 
 user@host> LD_PRELOAD=/usr/local/lib/libfaketime.so.1 FAKETIME="-15d"
-DONT_FAKE_MONOTONIC=1 java -version
+FAKETIME_DONT_FAKE_MONOTONIC=1 java -version
 java version "1.8.0_111"
 Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
 Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
@@ -205,6 +205,22 @@ specified absolute time. The 'start at' format allows a 'relative' clock
 operation as described below in section 4d), but using a 'start at' time
 instead of an offset time.
 
+If the started process itself starts other (child) processes, they by default
+will start with the specified start-at-date again. If this is not what you need,
+set the environment variable FAKETIME_DONT_RESET=1. Try these examples to see
+the difference:
+
+LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
+  FAKETIME_DONT_RESET=1 \
+  /bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
+
+LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
+  /bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
+
+In the second example, the "date" command will always print the same time,
+while in the first example, with FAKETIME_DONT_RESET set, time will increment
+even though all the "date" commands are new processes.
+
 
 4d) Using offsets for relative dates
 ------------------------------------
@@ -224,6 +240,11 @@ optionally followed by a multiplier:
   export FAKETIME="-10m" sets the faked time 10 minutes behind the real time.
   export FAKETIME="+14d" sets the faked time to 14 days in the future.
 
+  Please note that if you need other multipliers (weeks, months etc.) or higher
+  precision (e.g., correct leap year handling), you should use either the
+  faketime wrapper or the GNU date command as shown in the first of the three
+  examples below.
+
 You now should understand the complete example we've used before:
 
 LD_PRELOAD=/usr/local/lib/libfaketime.so.1 FAKETIME="-15d" date
@@ -281,6 +302,11 @@ completely independently of the system clock. It helps running programs
 with some determinism. In this single case all spawned processes will use
 the same global clock without restarting it at the start of each process.
 
+Please note that using "x" or "i" in FAKETIME still requires giving an offset
+(see section 4d). This means that "+1y x2" will work, but "x2" only will not.
+If you do not want to fake the time, but just modify clock speed, use something
+like "+0 x2", i.e., use an explizit zero offset as a prefix in your FAKETIME.
+
 For testing, your should run a command like
 
 LD_PRELOAD=./libfaketime.so.1 FAKETIME="+1,5y x10,0" \
@@ -326,6 +352,30 @@ with FAKETIME_NO_CACHE=1. Remember that disabling the cache may negatively
 influence the performance.
 
 
+Setting FAKETIME by means of a file timestamp
+---------------------------------------------
+
+Based on a proposal by Hitoshi Harada (umitanuki), the "start at" time can now be
+set through any file in the file system by setting the FAKETIME environment variable
+to "%" (a percent sign) and FAKETIME_FOLLOW_FILE to the name of the file whose
+modification timestamp shall be used as source for the "start at" time.
+
+Usage example:
+
+# create any file with December 24th, 2009, 12:34:56 as timestamp
+touch -t 0912241234.56 /tmp/my-demo-file.tmp
+
+# run a bash shell with an endless loop printing the current time
+LD_PRELOAD=/path/to/libfaketime.so.1 \
+  FAKETIME='%' FAKETIME_FOLLOW_FILE=/tmp/my-demo-file.tmp \
+  FAKETIME_DONT_RESET=1 \
+  bash -c 'while true ; do date ; sleep 1 ; done'
+
+# now, while the above is running, change the file's timestamp
+# (in a different terminal window or whatever)
+touch -t 2002290123.45 /tmp/my-demo-file.tmp
+
+
 4f) Faking the date and time system-wide
 ----------------------------------------
 
diff --git a/TODO b/TODO
index b8c801c..aeda10d 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,10 @@
 Open issues / next steps for libfaketime development
 
+- support interception of further system calls, e.g. clock_nanosleep (#105),
+  walltime (#195), chrono::system_clock::now (#176), select (#115)
+
+- fake timer_create and friends
 - add more functional tests that check more than the basic functionality
 - use the testing framework to also implement unit tests
-- fake timer_create and friends
 - work around thread local storage issue, e.g., by using pthreads
-- add autoconf/automake support to get rid of separate Makefile.OSX
-- improve pthread support
-
+- integrate manchicken's autoconf/automake support to get rid of separate Makefile.OSX
diff --git a/man/faketime.1 b/man/faketime.1
index 89c279d..039df19 100644
--- a/man/faketime.1
+++ b/man/faketime.1
@@ -1,4 +1,4 @@
-.TH FAKETIME "1" "November 2017" "faketime 0.9.7" wolfcw
+.TH FAKETIME "1" "August 2019" "faketime 0.9.8" wolfcw
 .SH NAME
 faketime \- manipulate the system time for a given command
 .SH SYNOPSIS
diff --git a/packaging/OSX/Fink/libfaketime.info b/packaging/OSX/Fink/libfaketime.info
index 6cf98f3..9f1b3ba 100644
--- a/packaging/OSX/Fink/libfaketime.info
+++ b/packaging/OSX/Fink/libfaketime.info
@@ -3,7 +3,7 @@ Version: 0.9.7
 Revision: 1
 
 Source: http://www.code-wizards.com/projects/%n/%n-%v.tar.gz
-Source-MD5: PLACEHOLDER
+Source-MD5: 8617e2c6caf0977b3ce9a271f867302c
 
 Maintainer: Wolfgang Hommel <wolf@fink.code-wizards.com>
 HomePage: https://github.com/wolfcw/libfaketime
diff --git a/packaging/OSX/Homebrew/libfaketime.rb b/packaging/OSX/Homebrew/libfaketime.rb
index 5e24456..8d92b76 100644
--- a/packaging/OSX/Homebrew/libfaketime.rb
+++ b/packaging/OSX/Homebrew/libfaketime.rb
@@ -3,7 +3,7 @@ require 'formula'
 class Libfaketime < Formula
   homepage 'https://github.com/wolfcw/libfaketime'
   url 'https://github.com/wolfcw/libfaketime/archive/v0.9.7.tar.gz'
-  sha1 'PLACEHOLDER'
+  sha1 'eb1cbacf548aefa36214bea1345f35b8763027a1'
 
   depends_on :macos => :sierra
 
diff --git a/packaging/OSX/MacPorts/Portfile b/packaging/OSX/MacPorts/Portfile
index 2196e04..d4104ad 100644
--- a/packaging/OSX/MacPorts/Portfile
+++ b/packaging/OSX/MacPorts/Portfile
@@ -6,8 +6,8 @@ PortGroup           github 1.0
 
 github.setup        wolfcw libfaketime 0.9.7 v
 
-checksums           rmd160  PLACEHOLDER \
-                    sha256  PLACEHOLDER
+checksums           rmd160  8874d43bb212976d929c3007db0cf978311b3596 \
+                    sha256  4d65f368b2d53ee2f93a25d5e9541ce27357f2b95e5e5afff210e0805042811e
 
 categories          sysutils
 platforms           darwin
diff --git a/src/Makefile b/src/Makefile
index a557c38..e2f047a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -9,12 +9,9 @@
 #     NO_ATFILE
 #         - Disables support for the fstatat() group of functions
 #
-#     PTHREAD
-#         - Define this to enable multithreading support.
-#
 #     PTHREAD_SINGLETHREADED_TIME
 #         - Define this if you want to single-thread time() ... there ARE
-#           possibile caching side-effects in a multithreaded environment
+#           possible caching side-effects in a multithreaded environment
 #           without this, but the performance impact may require you to
 #           try it unsynchronized.
 #
@@ -24,17 +21,20 @@
 #           that make use of low-level system calls, such as Java Virtual
 #           Machines.
 #
-# 	  FAKE_SLEEP
-# 	      - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
+#     FAKE_SLEEP
+#         - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
+#
+#     FAKE_TIMERS
+#         - Also intercept timer_settime() and timer_gettime()
 #
-#	  FAKE_TIMERS
-#	      - Also intercept timer_settime() and timer_gettime()
+#     FAKE_PTHREAD
+#         - Intercept pthread_cond_timedwait
 #
-#	  MULTI_ARCH
-#	  	  - If MULTI_ARCH is set, the faketime wrapper program will put a literal
-#	  	    $LIB into the LD_PRELOAD environment variable it creates, which makes
-#	  	    ld automatically choose the correct library version to use for the
-#	  	    target binary. Use for Linux platforms with Multi-Arch support only!
+#     MULTI_ARCH
+#  	  - If MULTI_ARCH is set, the faketime wrapper program will put a literal
+#           $LIB into the LD_PRELOAD environment variable it creates, which makes
+#           ld automatically choose the correct library version to use for the
+#           target binary. Use for Linux platforms with Multi-Arch support only!
 #
 #   * Compilation addition: second libMT target added for building the pthread-
 #     enabled library as a separate library
@@ -62,7 +62,7 @@ PREFIX ?= /usr/local
 LIBDIRNAME ?= /lib/faketime
 PLATFORM ?=$(shell uname)
 
-CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_STAT -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"'
+CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"'
 ifeq ($(PLATFORM),SunOS)
 CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=600
 endif
@@ -86,16 +86,16 @@ LIBS = libfaketime.so.${SONAME} libfaketimeMT.so.${SONAME}
 
 all: ${LIBS} ${BINS}
 
-libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD -DPTHREAD_SINGLETHREADED_TIME
+libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME
 
 ${LIBS_OBJ}: libfaketime.c
-	${CC} -o $@ -c ${CFLAGS} ${EXTRA_FLAGS} $<
+	${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $<
 
 %.so.${SONAME}: %.o libfaketime.map
 	${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ${LDADD}
 
 ${BINS}: faketime.c
-	${CC} -o $@ ${CFLAGS} ${EXTRA_FLAGS} $< ${LDFLAGS} ${BIN_LDFLAGS}
+	${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ${LDFLAGS} ${BIN_LDFLAGS}
 
 clean:
 	@rm -f ${LIBS_OBJ} ${LIBS} ${BINS}
diff --git a/src/Makefile.OSX b/src/Makefile.OSX
index 9d162af..3be1dd0 100644
--- a/src/Makefile.OSX
+++ b/src/Makefile.OSX
@@ -14,7 +14,7 @@
 #
 #     PTHREAD_SINGLETHREADED_TIME
 #         - Define this if you want to single-thread time() ... there ARE
-#           possibile caching side-effects in a multithreaded environment
+#           possible caching side-effects in a multithreaded environment
 #           without this, but the performance impact may require you to
 #           try it unsynchronized.
 #
@@ -39,7 +39,7 @@ INSTALL ?= install
 PREFIX ?= /usr/local
 
 CFLAGS += -DFAKE_SLEEP -DFAKE_INTERNAL_CALLS -DPREFIX='"'${PREFIX}'"'
-LIB_LDFLAGS += -dynamiclib -current_version 0.9.7 -compatibility_version 0.7
+LIB_LDFLAGS += -dynamiclib -current_version 0.9.8 -compatibility_version 0.7
 
 SONAME = 1
 LIBS = libfaketime.${SONAME}.dylib
diff --git a/src/faketime.c b/src/faketime.c
index 138ebbd..649d4b9 100644
--- a/src/faketime.c
+++ b/src/faketime.c
@@ -1,7 +1,7 @@
 /*
  *  libfaketime wrapper command
  *
- *  This file is part of libfaketime, version 0.9.7
+ *  This file is part of libfaketime, version 0.9.8
  *
  *  libfaketime is free software; you can redistribute it and/or modify it
  *  under the terms of the GNU General Public License v2 as published by the
@@ -124,7 +124,7 @@ int main (int argc, char **argv)
     }
     else if (0 == strcmp(argv[curr_opt], "--exclude-monotonic"))
     {
-      setenv("DONT_FAKE_MONOTONIC", "1", true);
+      setenv("FAKETIME_DONT_FAKE_MONOTONIC", "1", true);
       curr_opt++;
       continue;
     }
@@ -209,7 +209,7 @@ int main (int argc, char **argv)
     int shm_fd;
     sem_t *sem;
     struct ft_shared_s *ft_shared;
-    char shared_objs[PATH_BUFSIZE];
+    char shared_objs[PATH_BUFSIZE * 2 + 1];
 
     /*
      * Casting of getpid() return value to long needed to make GCC on SmartOS
@@ -286,7 +286,7 @@ int main (int argc, char **argv)
       exit(EXIT_FAILURE);
     }
 
-    snprintf(shared_objs, PATH_BUFSIZE, "%s %s", sem_name, shm_name);
+    snprintf(shared_objs, sizeof(shared_objs), "%s %s", sem_name, shm_name);
     setenv("FAKETIME_SHARED", shared_objs, true);
     sem_close(sem);
   }
diff --git a/src/libfaketime.c b/src/libfaketime.c
index eb2d01b..74c34e8 100644
--- a/src/libfaketime.c
+++ b/src/libfaketime.c
@@ -1,5 +1,5 @@
 /*
- *  This file is part of libfaketime, version 0.9.7
+ *  This file is part of libfaketime, version 0.9.8
  *
  *  libfaketime is free software; you can redistribute it and/or modify it
  *  under the terms of the GNU General Public License v2 as published by the
@@ -41,11 +41,14 @@
 #include <netinet/in.h>
 #include <limits.h>
 
+#include "uthash.h"
+
 #include "time_ops.h"
 #include "faketime_common.h"
 
+
 /* pthread-handling contributed by David North, TDI in version 0.7 */
-#ifdef PTHREAD
+#if defined PTHREAD_SINGLETHREADED_TIME || defined FAKE_PTHREAD
 #include <pthread.h>
 #endif
 
@@ -140,6 +143,14 @@ static int          (*real___ftime)           (struct timeb *);
 static int          (*real___gettimeofday)    (struct timeval *, void *);
 static int          (*real___clock_gettime)   (clockid_t clk_id, struct timespec *tp);
 #endif
+#ifdef FAKE_PTHREAD
+static int          (*real_pthread_cond_timedwait_225)  (pthread_cond_t *, pthread_mutex_t*, struct timespec *);
+static int          (*real_pthread_cond_timedwait_232)  (pthread_cond_t *, pthread_mutex_t*, struct timespec *);
+static int          (*real_pthread_cond_init_232) (pthread_cond_t *restrict, const pthread_condattr_t *restrict);
+static int          (*real_pthread_cond_destroy_232) (pthread_cond_t *);
+static pthread_rwlock_t monotonic_conds_lock;
+#endif
+
 #ifndef __APPLEOSX__
 #ifdef FAKE_TIMERS
 static int          (*real_timer_settime_22)   (int timerid, int flags, const struct itimerspec *new_value,
@@ -220,7 +231,11 @@ static char ft_spawn_target[1024];
 static long ft_spawn_secs = -1;
 static long ft_spawn_ncalls = -1;
 
+#ifdef __ARM_ARCH
+static int fake_monotonic_clock = 0;
+#else
 static int fake_monotonic_clock = 1;
+#endif
 static int cache_enabled = 1;
 static int cache_duration = 10;     /* cache fake time input for 10 seconds */
 
@@ -249,14 +264,15 @@ static double user_rate = 1.0;
 static bool user_rate_set = false;
 static struct timespec user_per_tick_inc = {0, -1};
 static bool user_per_tick_inc_set = false;
+static bool user_per_tick_inc_set_backup = false;
 
 enum ft_mode_t {FT_FREEZE, FT_START_AT, FT_NOOP} ft_mode = FT_FREEZE;
 
 /* Time to fake is not provided through FAKETIME env. var. */
 static bool parse_config_file = true;
 
-void ft_cleanup (void) __attribute__ ((destructor));
-void ftpl_init (void) __attribute__ ((constructor));
+static void ft_cleanup (void) __attribute__ ((destructor));
+static void ftpl_init (void) __attribute__ ((constructor));
 
 
 /*
@@ -265,41 +281,180 @@ void ftpl_init (void) __attribute__ ((constructor));
  *      =======================================================================
  */
 
+static bool shmCreator = false;
+
+static void ft_shm_create(void) {
+  char sem_name[256], shm_name[256], sem_nameT[256], shm_nameT[256];
+  int shm_fdN;
+  sem_t *semN;
+  struct ft_shared_s *ft_sharedN;
+  char shared_objsN[513];
+  sem_t *shared_semT = NULL;
+
+  snprintf(sem_name, 255, "/faketime_sem_%ld", (long)getpid());
+  snprintf(shm_name, 255, "/faketime_shm_%ld", (long)getpid());
+  if (SEM_FAILED == (semN = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
+  {  
+    perror("libfaketime: In ft_shm_create(), sem_open failed");
+    fprintf(stderr, "libfaketime: attempted sem_name was %s\n", sem_name);
+    exit(EXIT_FAILURE);
+  }
+  /* create shm */
+  if (-1 == (shm_fdN = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR)))
+  {  
+    perror("libfaketime: In ft_shm_create(), shm_open failed");
+    exit(EXIT_FAILURE);
+  }
+  /* set shm size */
+  if (-1 == ftruncate(shm_fdN, sizeof(uint64_t)))
+  {
+    perror("libfaketime: In ft_shm_create(), ftruncate failed");
+    exit(EXIT_FAILURE);
+  }  
+  /* map shm */
+  if (MAP_FAILED == (ft_sharedN = mmap(NULL, sizeof(struct ft_shared_s), PROT_READ|PROT_WRITE,
+                     MAP_SHARED, shm_fdN, 0)))
+  {
+    perror("libfaketime: In ft_shm_create(), mmap failed");
+    exit(EXIT_FAILURE);
+  }
+  if (sem_wait(semN) == -1)
+  {
+    perror("libfaketime: In ft_shm_create(), sem_wait failed");
+    exit(EXIT_FAILURE);
+  }
+  /* init elapsed time ticks to zero */
+  ft_sharedN->ticks = 0;
+  ft_sharedN->file_idx = 0;
+  ft_sharedN->start_time.real.tv_sec = 0;
+  ft_sharedN->start_time.real.tv_nsec = -1;
+  ft_sharedN->start_time.mon.tv_sec = 0;
+  ft_sharedN->start_time.mon.tv_nsec = -1;
+  ft_sharedN->start_time.mon_raw.tv_sec = 0;
+  ft_sharedN->start_time.mon_raw.tv_nsec = -1;
+
+  if (-1 == munmap(ft_sharedN, (sizeof(struct ft_shared_s))))
+  {
+    perror("libfaktime: In ft_shm_create(), munmap failed");
+    exit(EXIT_FAILURE);
+  }
+  if (sem_post(semN) == -1)
+  {
+    perror("libfaketime: In ft_shm_create(), sem_post failed");
+    exit(EXIT_FAILURE);
+  } 
+
+  snprintf(shared_objsN, sizeof(shared_objsN), "%s %s", sem_name, shm_name);
+
+  int semSafetyCheckPassed = 0;
+  sem_close(semN);
+
+  sscanf(shared_objsN, "%255s %255s", sem_nameT, shm_nameT);
+  if (SEM_FAILED == (shared_semT = sem_open(sem_nameT, 0)))
+  {
+      fprintf(stderr, "libfaketime: In ft_shm_create(), non-fatal sem_open issue with %s", sem_nameT);
+  }
+  else {
+    semSafetyCheckPassed = 1;
+    sem_close(shared_semT);
+  }
+
+  if (semSafetyCheckPassed == 1) {
+    setenv("FAKETIME_SHARED", shared_objsN, true);
+    shmCreator = true;
+  }
+}
+
+static void ft_shm_destroy(void)
+{
+  char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED");
+
+  if (ft_shared_env != NULL)
+  {
+    if (sscanf(ft_shared_env, "%255s %255s", sem_name, shm_name) < 2)
+    {
+      printf("libfaktime: In ft_shm_destroy(), error parsing semaphore name and shared memory id from string: %s", ft_shared_env);
+      exit(1);
+    }
+    /*
+       To avoid shared memory / semaphores left after quitting, we have to clean
+       up here similar to how the faketime wrapper does.
+       However, there is no guarantee that all child processes have quit before
+       we clean up here, which potentially leaves us in a stale state.
+       Since there is no easy solution for this problem (see issue #56),
+       ft_shm_init() below at least tries to handle this carefully.
+    */
+    sem_unlink(sem_name);
+    sem_unlink(shm_name);
+    unsetenv("FAKETIME_SHARED");
+  }
+}
+
 static void ft_shm_init (void)
 {
   int ticks_shm_fd;
   char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED");
+  sem_t *shared_semR = NULL;
 
+  /* create semaphore and shared memory locally unless it has been passed along */
+  if (ft_shared_env == NULL)
+  {
+    ft_shm_create();
+    ft_shared_env = getenv("FAKETIME_SHARED");
+  }
+  
+  /* check for stale semaphore / shared memory information */
   if (ft_shared_env != NULL)
   {
     if (sscanf(ft_shared_env, "%255s %255s", sem_name, shm_name) < 2)
     {
-      printf("Error parsing semaphore name and shared memory id from string: %s", ft_shared_env);
+      printf("libfaketime: In ft_shm_init(), error parsing semaphore name and shared memory id from string: %s", ft_shared_env);
+      exit(1);
+    }
+    if (SEM_FAILED == (shared_semR = sem_open(sem_name, 0))) /* gone stale? */
+    {
+      ft_shm_create();
+      ft_shared_env = getenv("FAKETIME_SHARED");
+    }
+    else 
+    {
+      sem_close(shared_semR);
+    }
+  }
+
+  /* process the semaphore / shared memory information */
+  if (ft_shared_env != NULL)
+  {
+    if (sscanf(ft_shared_env, "%255s %255s", sem_name, shm_name) < 2)
+    {
+      printf("libfaketime: In ft_shm_init(), error parsing semaphore name and shared memory id from string: %s", ft_shared_env);
       exit(1);
     }
 
     if (SEM_FAILED == (shared_sem = sem_open(sem_name, 0)))
     {
-      perror("sem_open");
+      perror("libfaketime: In ft_shm_init(), sem_open failed");
+      fprintf(stderr, "libfaketime: sem_name was %s, created locally: %s\n", sem_name, shmCreator ? "true":"false");
+      fprintf(stderr, "libfaketime: parsed from env: %s\n", ft_shared_env);
       exit(1);
     }
 
     if (-1 == (ticks_shm_fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)))
     {
-      perror("shm_open");
+      perror("libfaketime: In ft_shm_init(), shm_open failed");
       exit(1);
     }
 
     if (MAP_FAILED == (ft_shared = mmap(NULL, sizeof(struct ft_shared_s), PROT_READ|PROT_WRITE,
             MAP_SHARED, ticks_shm_fd, 0)))
     {
-      perror("mmap");
+      perror("libfaketime: In ft_shm_init(), mmap failed");
       exit(1);
     }
   }
 }
 
-void ft_cleanup (void)
+static void ft_cleanup (void)
 {
   /* detach from shared memory */
   if (ft_shared != NULL)
@@ -314,6 +469,13 @@ void ft_cleanup (void)
   {
     sem_close(shared_sem);
   }
+#ifdef FAKE_PTHREAD
+  if (pthread_rwlock_destroy(&monotonic_conds_lock) != 0) {
+    fprintf(stderr, "libfaketime: In ft_cleanup(), monotonic_conds_lock destroy failed\n");
+    exit(-1);
+  }
+#endif
+  if (shmCreator == true) ft_shm_destroy();
 }
 
 
@@ -363,7 +525,7 @@ static void next_time(struct timespec *tp, struct timespec *ticklen)
     /* lock */
     if (sem_wait(shared_sem) == -1)
     {
-      perror("sem_wait");
+      perror("libfaketime: In next_time(), sem_wait failed");
       exit(1);
     }
     /* calculate and update elapsed time */
@@ -373,7 +535,7 @@ static void next_time(struct timespec *tp, struct timespec *ticklen)
     /* unlock */
     if (sem_post(shared_sem) == -1)
     {
-      perror("sem_post");
+      perror("libfaketime: In next_time(), sem_post failed");
       exit(1);
     }
   }
@@ -400,7 +562,7 @@ static void save_time(struct timespec *tp)
     /* lock */
     if (sem_wait(shared_sem) == -1)
     {
-      perror("sem_wait");
+      perror("libfaketime: In save_time(), sem_wait failed");
       exit(1);
     }
 
@@ -414,13 +576,13 @@ static void save_time(struct timespec *tp)
 
     if ((written == -1) || (n < sizeof(time_write)))
     {
-      perror("Saving timestamp to file failed");
+      perror("libfaketime: In save_time(), saving timestamp to file failed");
     }
 
     /* unlock */
     if (sem_post(shared_sem) == -1)
     {
-      perror("sem_post");
+      perror("libfaketime: In save_time(), sem_post failed");
       exit(1);
     }
   }
@@ -438,7 +600,7 @@ static bool load_time(struct timespec *tp)
     /* lock */
     if (sem_wait(shared_sem) == -1)
     {
-      perror("sem_wait");
+      perror("libfaketime: In load_time(), sem_wait failed");
       exit(1);
     }
 
@@ -473,7 +635,7 @@ static bool load_time(struct timespec *tp)
     /* unlock */
     if (sem_post(shared_sem) == -1)
     {
-      perror("sem_post");
+      perror("libfaketime: In load_time(), sem_post failed");
       exit(1);
     }
   }
@@ -500,6 +662,36 @@ static bool load_time(struct timespec *tp)
 
 static int fake_stat_disabled = 0;
 
+void lock_for_stat()
+{
+  if (shared_sem != NULL)
+  {
+    if (sem_wait(shared_sem) == -1)
+    {
+      perror("libfaketime: In lock_for_stat(), sem_wait failed");
+      exit(1);
+    }
+  }
+  user_per_tick_inc_set_backup = user_per_tick_inc_set;
+  user_per_tick_inc_set = false;
+  return;
+}
+
+void unlock_for_stat()
+{
+  user_per_tick_inc_set = user_per_tick_inc_set_backup;
+
+  if (shared_sem != NULL)
+  {
+    if (sem_post(shared_sem) == -1)
+    {
+      perror("libfaketime: In unlock_for_stat(), sem_post failed");
+      exit(1);
+    }
+  }
+  return;
+}
+
 #define FAKE_STRUCT_STAT_TIME(which) {                \
     struct timespec t = {buf->st_##which##time,       \
                          buf->st_##which##timensec};  \
@@ -510,25 +702,33 @@ static int fake_stat_disabled = 0;
 
 static inline void fake_statbuf (struct stat *buf) {
 #ifndef st_atime
+  lock_for_stat();
   FAKE_STRUCT_STAT_TIME(c);
   FAKE_STRUCT_STAT_TIME(a);
   FAKE_STRUCT_STAT_TIME(m);
+  unlock_for_stat();
 #else
+  lock_for_stat();
   fake_clock_gettime(CLOCK_REALTIME, &buf->st_ctim);
   fake_clock_gettime(CLOCK_REALTIME, &buf->st_atim);
   fake_clock_gettime(CLOCK_REALTIME, &buf->st_mtim);
+  unlock_for_stat();
 #endif
 }
 
 static inline void fake_stat64buf (struct stat64 *buf) {
 #ifndef st_atime
+  lock_for_stat();
   FAKE_STRUCT_STAT_TIME(c);
   FAKE_STRUCT_STAT_TIME(a);
   FAKE_STRUCT_STAT_TIME(m);
+  unlock_for_stat();
 #else
+  lock_for_stat();
   fake_clock_gettime(CLOCK_REALTIME, &buf->st_ctim);
   fake_clock_gettime(CLOCK_REALTIME, &buf->st_atim);
   fake_clock_gettime(CLOCK_REALTIME, &buf->st_mtim);
+  unlock_for_stat();
 #endif
 }
 
@@ -1092,7 +1292,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
   {
     struct timespec tdiff, timeadj;
 
-    timespecsub(abs_timeout, &ftpl_starttime.real, &tdiff);
+    timespecsub(abs_timeout, &user_faked_time_timespec, &tdiff);
 
     if (user_rate_set)
     {
@@ -1102,7 +1302,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
     {
         timeadj = tdiff;
     }
-    timespecadd(&user_faked_time_timespec, &timeadj, &real_abs_timeout);
+    timespecadd(&ftpl_starttime.real, &timeadj, &real_abs_timeout);
     real_abs_timeout_pt = &real_abs_timeout;
   }
   else
@@ -1128,7 +1328,7 @@ typedef union {
 /*
  * Faketime's function implementation's compatibility mode
  */
-typedef enum {FT_COMPAT_GLIBC_2_2, FT_COMPAT_GLIBC_2_3_3} ft_lib_compat;
+typedef enum {FT_COMPAT_GLIBC_2_2, FT_COMPAT_GLIBC_2_3_3} ft_lib_compat_timer;
 
 
 /*
@@ -1138,7 +1338,7 @@ typedef enum {FT_COMPAT_GLIBC_2_2, FT_COMPAT_GLIBC_2_3_3} ft_lib_compat;
 static int
 timer_settime_common(timer_t_or_int timerid, int flags,
          const struct itimerspec *new_value,
-         struct itimerspec *old_value, ft_lib_compat compat)
+         struct itimerspec *old_value, ft_lib_compat_timer compat)
 {
   int result;
   struct itimerspec new_real;
@@ -1293,7 +1493,7 @@ int timer_settime_233(timer_t timerid, int flags,
  * Faked timer_gettime()
  * Does not affect timer speed when stepping clock with each time() call.
  */
-int timer_gettime_common(timer_t_or_int timerid, struct itimerspec *curr_value, ft_lib_compat compat)
+int timer_gettime_common(timer_t_or_int timerid, struct itimerspec *curr_value, ft_lib_compat_timer compat)
 {
   int result;
 
@@ -1521,7 +1721,14 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
   if (result == -1) return result; /* original function failed */
 
   /* pass the real current time to our faking version, overwriting it */
-  if (fake_monotonic_clock || clk_id != CLOCK_MONOTONIC)
+  if (fake_monotonic_clock || (clk_id != CLOCK_MONOTONIC && clk_id != CLOCK_MONOTONIC_RAW
+#ifdef CLOCK_MONOTONIC_COARSE
+      && clk_id != CLOCK_MONOTONIC_COARSE
+#endif
+#ifdef CLOCK_BOOTTIME
+      && clk_id != CLOCK_BOOTTIME
+#endif
+      ))
   {
     result = fake_clock_gettime(clk_id, tp);
   }
@@ -1541,6 +1748,7 @@ static void parse_ft_string(const char *user_faked_time)
 {
   struct tm user_faked_time_tm;
   char * tmp_time_fmt;
+  char * nstime_str;
 
   if (!strncmp(user_faked_time, user_faked_time_saved, BUFFERLEN))
   {
@@ -1555,15 +1763,24 @@ static void parse_ft_string(const char *user_faked_time)
     default:  /* Try and interpret this as a specified time */
       if (ft_mode != FT_NOOP) ft_mode = FT_FREEZE;
       user_faked_time_tm.tm_isdst = -1;
-      if (NULL != strptime(user_faked_time, user_faked_time_fmt, &user_faked_time_tm))
+      nstime_str = strptime(user_faked_time, user_faked_time_fmt, &user_faked_time_tm);
+
+      if (NULL != nstime_str)
       {
         user_faked_time_timespec.tv_sec = mktime(&user_faked_time_tm);
         user_faked_time_timespec.tv_nsec = 0;
+
+        if (nstime_str[0] == '.')
+        {
+          double nstime = atof(--nstime_str);
+          user_faked_time_timespec.tv_nsec = (nstime - floor(nstime)) * SEC_TO_nSEC;
+        }
         user_faked_time_set = true;
       }
       else
       {
-        perror("Failed to parse FAKETIME timestamp");
+        perror("libfaketime: In parse_ft_string(), failed to parse FAKETIME timestamp");
+        fprintf(stderr, "Please check specification %s with format %s\n", user_faked_time, user_faked_time_fmt);
         exit(EXIT_FAILURE);
       }
       break;
@@ -1592,13 +1809,55 @@ static void parse_ft_string(const char *user_faked_time)
     case '@': /* Specific time, but clock along relative to that starttime */
       ft_mode = FT_START_AT;
       user_faked_time_tm.tm_isdst = -1;
-      (void) strptime(&user_faked_time[1], user_faked_time_fmt, &user_faked_time_tm);
+      nstime_str = strptime(&user_faked_time[1], user_faked_time_fmt, &user_faked_time_tm);
+      if (NULL != nstime_str)
+      {
+        user_faked_time_timespec.tv_sec = mktime(&user_faked_time_tm);
+        user_faked_time_timespec.tv_nsec = 0;
 
-      user_faked_time_timespec.tv_sec = mktime(&user_faked_time_tm);
-      user_faked_time_timespec.tv_nsec = 0;
+        if (nstime_str[0] == '.')
+        {
+          double nstime = atof(--nstime_str);
+          user_faked_time_timespec.tv_nsec = (nstime - floor(nstime)) * SEC_TO_nSEC;
+        }
+      }
+      else
+      {
+        perror("libfaketime: In parse_ft_string(), failed to parse FAKETIME timestamp");
+        exit(EXIT_FAILURE);
+      }
 
       /* Reset starttime */
-      system_time_from_system(&ftpl_starttime);
+      if (NULL == getenv("FAKETIME_DONT_RESET"))
+        system_time_from_system(&ftpl_starttime);
+      goto parse_modifiers;
+      break;
+
+    case '%': /* follow file timestamp as suggested by Hitoshi Harada (umitanuki) */
+      ft_mode = FT_START_AT;
+      struct stat master_file_stats;
+      int ret;
+      if (NULL == getenv("FAKETIME_FOLLOW_FILE"))
+      {
+        fprintf(stderr, "libfaketime: %% operator in FAKETIME setting requires environment variable FAKETIME_FOLLOW_FILE set.\n");
+        exit(1);
+      }
+      else
+      {
+        DONT_FAKE_TIME(ret = stat(getenv("FAKETIME_FOLLOW_FILE"), &master_file_stats));
+        if (ret == -1)
+        {
+          fprintf(stderr, "libfaketime: Cannot get timestamp of file %s as requested by %% operator.\n", getenv("FAKETIME_FOLLOW_FILE"));
+          exit(1);
+        }
+        else
+        {
+          user_faked_time_timespec.tv_sec = master_file_stats.st_mtime;
+          user_faked_time_timespec.tv_nsec = 0;
+        }
+      }
+      if (NULL == getenv("FAKETIME_DONT_RESET"))
+        system_time_from_system(&ftpl_starttime);
       goto parse_modifiers;
       break;
 
@@ -1636,11 +1895,15 @@ parse_modifiers:
  *      =======================================================================
  */
 
-void ftpl_init(void)
+static void ftpl_init(void)
 {
   char *tmp_env;
   bool dont_fake_final;
 
+  /* moved up here from below the dlsym calls #130 */
+  dont_fake = true; // Do not fake times during initialization
+  dont_fake_final = false;
+
 #ifdef __APPLE__
   const char *progname = getprogname();
 #else
@@ -1658,7 +1921,11 @@ void ftpl_init(void)
   real_lstat64 =            dlsym(RTLD_NEXT, "__lxstat64");
   real_time =               dlsym(RTLD_NEXT, "time");
   real_ftime =              dlsym(RTLD_NEXT, "ftime");
+#if defined(__alpha__) && defined(__GLIBC__)
+  real_gettimeofday =       dlvsym(RTLD_NEXT, "gettimeofday", "GLIBC_2.1");
+#else
   real_gettimeofday =       dlsym(RTLD_NEXT, "gettimeofday");
+#endif
 #ifdef FAKE_SLEEP
   real_nanosleep =          dlsym(RTLD_NEXT, "nanosleep");
   real_usleep =             dlsym(RTLD_NEXT, "usleep");
@@ -1671,9 +1938,41 @@ void ftpl_init(void)
 #endif
 #ifdef FAKE_INTERNAL_CALLS
   real___ftime =              dlsym(RTLD_NEXT, "__ftime");
+#  if defined(__alpha__) && defined(__GLIBC__)
+  real___gettimeofday =       dlvsym(RTLD_NEXT, "__gettimeofday", "GLIBC_2.1");
+#  else
   real___gettimeofday =       dlsym(RTLD_NEXT, "__gettimeofday");
+#  endif
   real___clock_gettime  =     dlsym(RTLD_NEXT, "__clock_gettime");
 #endif
+#ifdef FAKE_PTHREAD
+
+#ifdef __GLIBC__
+  real_pthread_cond_timedwait_225 = dlvsym(RTLD_NEXT, "pthread_cond_timedwait", "GLIBC_2.2.5");
+
+  real_pthread_cond_timedwait_232 = dlvsym(RTLD_NEXT, "pthread_cond_timedwait", "GLIBC_2.3.2");
+  real_pthread_cond_init_232 = dlvsym(RTLD_NEXT, "pthread_cond_init", "GLIBC_2.3.2");
+  real_pthread_cond_destroy_232 = dlvsym(RTLD_NEXT, "pthread_cond_destroy", "GLIBC_2.3.2");
+#endif
+
+  if (NULL == real_pthread_cond_timedwait_232)
+  {
+    real_pthread_cond_timedwait_232 =  dlsym(RTLD_NEXT, "pthread_cond_timedwait");
+  }
+  if (NULL == real_pthread_cond_init_232)
+  {
+    real_pthread_cond_init_232 =  dlsym(RTLD_NEXT, "pthread_cond_init");
+  }
+  if (NULL == real_pthread_cond_destroy_232)
+  {
+    real_pthread_cond_destroy_232 =  dlsym(RTLD_NEXT, "pthread_cond_destroy");
+  }
+
+  if (pthread_rwlock_init(&monotonic_conds_lock,NULL) != 0) {
+    fprintf(stderr,"monotonic_conds_lock init failed\n");
+    exit(-1);
+  }
+#endif
 #ifdef __APPLEOSX__
   real_clock_get_time =     dlsym(RTLD_NEXT, "clock_get_time");
   real_clock_gettime  =     apple_clock_gettime;
@@ -1688,14 +1987,18 @@ void ftpl_init(void)
     real_timer_gettime_233 =  dlsym(RTLD_NEXT, "timer_gettime");
     real_timer_settime_233 =  dlsym(RTLD_NEXT, "timer_settime");
 #else
+#ifdef __GLIBC__
   real_timer_settime_22 =   dlvsym(RTLD_NEXT, "timer_settime","GLIBC_2.2");
   real_timer_settime_233 =  dlvsym(RTLD_NEXT, "timer_settime","GLIBC_2.3.3");
+#endif
   if (NULL == real_timer_settime_233)
   {
     real_timer_settime_233 =  dlsym(RTLD_NEXT, "timer_settime");
   }
+#ifdef __GLIBC__
   real_timer_gettime_22 =   dlvsym(RTLD_NEXT, "timer_gettime","GLIBC_2.2");
   real_timer_gettime_233 =  dlvsym(RTLD_NEXT, "timer_gettime","GLIBC_2.3.3");
+#endif
   if (NULL == real_timer_gettime_233)
   {
     real_timer_gettime_233 =  dlsym(RTLD_NEXT, "timer_gettime");
@@ -1704,8 +2007,6 @@ void ftpl_init(void)
 #endif
 #endif
 
-  dont_fake = true; // Do not fake times during initialization
-  dont_fake_final = false;
   initialized = 1;
 
   ft_shm_init();
@@ -1727,7 +2028,8 @@ void ftpl_init(void)
       cache_enabled = 0;
     }
   }
-  if ((tmp_env = getenv("DONT_FAKE_MONOTONIC")) != NULL)
+  if ((tmp_env = getenv("FAKETIME_DONT_FAKE_MONOTONIC")) != NULL
+    || (tmp_env = getenv("DONT_FAKE_MONOTONIC")) != NULL)
   {
     if (0 == strcmp(tmp_env, "1"))
     {
@@ -1828,7 +2130,8 @@ void ftpl_init(void)
   if ((tmp_env = getenv("FAKETIME_SPAWN_TARGET")) != NULL)
   {
     spawnsupport = true;
-    (void) strncpy(ft_spawn_target, getenv("FAKETIME_SPAWN_TARGET"), 1024);
+    (void) strncpy(ft_spawn_target, getenv("FAKETIME_SPAWN_TARGET"), sizeof(ft_spawn_target) - 1);
+    ft_spawn_target[sizeof(ft_spawn_target) - 1] = 0;
     if ((tmp_env = getenv("FAKETIME_SPAWN_SECONDS")) != NULL)
     {
       ft_spawn_secs = atol(tmp_env);
@@ -1844,7 +2147,7 @@ void ftpl_init(void)
     if (-1 == (outfile = open(tmp_env, O_RDWR | O_APPEND | O_CLOEXEC | O_CREAT,
                               S_IWUSR | S_IRUSR)))
     {
-      perror("Opening file for saving timestamps failed");
+      perror("libfaketime: In ftpl_init(), opening file for saving timestamps failed");
       exit(EXIT_FAILURE);
     }
   }
@@ -1856,7 +2159,7 @@ void ftpl_init(void)
     struct stat sb;
     if (-1 == (infile = open(tmp_env, O_RDONLY|O_CLOEXEC)))
     {
-      perror("Opening file for loading timestamps failed");
+      perror("libfaketime: In ftpl_init(), opening file for loading timestamps failed");
       exit(EXIT_FAILURE);
     }
 
@@ -1876,7 +2179,7 @@ void ftpl_init(void)
     stss = mmap(NULL, infile_size, PROT_READ, MAP_SHARED, infile, 0);
     if (stss == MAP_FAILED)
     {
-      perror("Mapping file for loading timestamps failed");
+      perror("libfaketime: In ftpl_init(), mapping file for loading timestamps failed");
       exit(EXIT_FAILURE);
     }
     infile_set = true;
@@ -1889,14 +2192,15 @@ void ftpl_init(void)
   }
   else
   {
-    strncpy(user_faked_time_fmt, tmp_env, BUFSIZ);
+    strncpy(user_faked_time_fmt, tmp_env, BUFSIZ - 1);
+    user_faked_time_fmt[BUFSIZ - 1] = 0;
   }
 
   if (shared_sem != 0)
   {
     if (sem_wait(shared_sem) == -1)
     {
-      perror("sem_wait");
+      perror("libfaketime: In ftpl_init(), sem_wait failed");
       exit(1);
     }
     if (ft_shared->start_time.real.tv_nsec == -1)
@@ -1912,7 +2216,7 @@ void ftpl_init(void)
     }
     if (sem_post(shared_sem) == -1)
     {
-      perror("sem_post");
+      perror("libfaketime: In ftpl_init(), sem_post failed");
       exit(1);
     }
   }
@@ -1960,6 +2264,14 @@ static void remove_trailing_eols(char *line)
  *      =======================================================================
  */
 
+#ifdef PTHREAD_SINGLETHREADED_TIME
+static void pthread_cleanup_mutex_lock(void *data)
+{
+  pthread_mutex_t *mutex = data;
+  pthread_mutex_unlock(mutex);
+}
+#endif
+
 int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
 {
   /* variables used for caching, introduced in version 0.6 */
@@ -1983,7 +2295,7 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
 #ifdef PTHREAD_SINGLETHREADED_TIME
   static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER;
   pthread_mutex_lock(&time_mutex);
-  pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, (void *)&time_mutex);
+  pthread_cleanup_push(pthread_cleanup_mutex_lock, &time_mutex);
 #endif
 
   if ((limited_faking &&
@@ -2020,8 +2332,8 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
         break;
 #endif
       default:
-        printf("Invalid clock_id for clock_gettime: %d", clk_id);
-        exit(EXIT_FAILURE);
+        timespecsub(tp, &ftpl_starttime.real, &tmp_ts);
+        break;
     }
 
     if (limited_faking)
@@ -2049,9 +2361,12 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
     }
   }
 
+  struct timespec current_ts;
+  DONT_FAKE_TIME((*real_clock_gettime)(CLOCK_REALTIME, &current_ts));
+
   if (last_data_fetch > 0)
   {
-    if ((tp->tv_sec - last_data_fetch) > cache_duration)
+    if ((current_ts.tv_sec - last_data_fetch) > cache_duration)
     {
       cache_expired = 1;
     }
@@ -2078,14 +2393,15 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
 
     if (NULL != (tmp_env = getenv("FAKETIME")))
     {
-      strncpy(user_faked_time, tmp_env, BUFFERLEN);
+      strncpy(user_faked_time, tmp_env, BUFFERLEN - 1);
+      user_faked_time[BUFFERLEN - 1] = 0;
     }
     else
     {
       snprintf(user_faked_time, BUFFERLEN, "+0");
     }
 
-    last_data_fetch = tp->tv_sec;
+    last_data_fetch = current_ts.tv_sec;
     /* fake time supplied as environment variable? */
     if (parse_config_file)
     {
@@ -2171,8 +2487,8 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
             break;
 #endif
           default:
-            printf("Invalid clock_id for clock_gettime: %d", clk_id);
-            exit(EXIT_FAILURE);
+            timespecsub(tp, &ftpl_starttime.real, &tdiff);
+            break;
         } // end of switch (clk_id)
         if (user_rate_set)
         {
@@ -2327,7 +2643,15 @@ int __clock_gettime(clockid_t clk_id, struct timespec *tp)
   if (result == -1) return result; /* original function failed */
 
   /* pass the real current time to our faking version, overwriting it */
-  if (fake_monotonic_clock || clk_id != CLOCK_MONOTONIC)
+  if (fake_monotonic_clock || (clk_id != CLOCK_MONOTONIC && clk_id != CLOCK_MONOTONIC_RAW
+#ifdef CLOCK_MONOTONIC_COARSE
+      && clk_id != CLOCK_MONOTONIC_COARSE
+#endif
+#ifdef CLOCK_BOOTTIME
+      && clk_id != CLOCK_BOOTTIME
+#endif
+      ))
+
   {
     result = fake_clock_gettime(clk_id, tp);
   }
@@ -2394,6 +2718,197 @@ int __ftime(struct timeb *tb)
 
 #endif
 
+/*
+ *      =======================================================================
+ *      Faked pthread_cond_timedwait                          === FAKE(pthread)
+ *      =======================================================================
+ */
+
+/* pthread_cond_timedwait
+
+   The specified absolute time in pthread_cond_timedwait is directly
+   passed to the kernel via the futex syscall. The kernel, however,
+   does not know about the fake time. In 99.9% of cases, the time
+   until this function should wait is calculated by an application
+   relatively to the current time, which has been faked in the
+   application. Hence, we should convert the waiting time back to real
+   time.
+
+   pthread_cond_timedwait in GLIBC_2_2_5 only supports
+   CLOCK_REALTIME.  Since the init and destroy functions are not
+   redefined for GLIBC_2_2_5, a corresponding cond will never be
+   added to monotonic_conds and hence the correct branch will
+   always be taken.
+*/
+
+
+#ifdef FAKE_PTHREAD
+
+typedef enum {FT_COMPAT_GLIBC_2_2_5, FT_COMPAT_GLIBC_2_3_2} ft_lib_compat_pthread;
+
+struct pthread_cond_monotonic {
+    pthread_cond_t *ptr;
+    UT_hash_handle hh;
+};
+
+static struct pthread_cond_monotonic *monotonic_conds = NULL;
+
+int pthread_cond_init_232(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr)
+{
+  clockid_t clock_id;
+  int result;
+
+  result = real_pthread_cond_init_232(cond, attr);
+
+  if (result != 0 || attr == NULL)
+    return result;
+
+  pthread_condattr_getclock(attr, &clock_id);
+
+  if (clock_id == CLOCK_MONOTONIC) {
+    struct pthread_cond_monotonic *e = (struct pthread_cond_monotonic*)malloc(sizeof(struct pthread_cond_monotonic));
+    e->ptr = cond;
+
+    if (pthread_rwlock_wrlock(&monotonic_conds_lock) != 0) {
+      fprintf(stderr,"can't acquire write monotonic_conds_lock\n");
+      exit(-1);
+    }
+    HASH_ADD_PTR(monotonic_conds, ptr, e);
+    pthread_rwlock_unlock(&monotonic_conds_lock);
+  }
+
+  return result;
+}
+
+int pthread_cond_destroy_232(pthread_cond_t *cond)
+{
+  struct pthread_cond_monotonic* e;
+
+  if (pthread_rwlock_wrlock(&monotonic_conds_lock) != 0) {
+    fprintf(stderr,"can't acquire write monotonic_conds_lock\n");
+    exit(-1);
+  }
+  HASH_FIND_PTR(monotonic_conds, &cond, e);
+  if (e) {
+    HASH_DEL(monotonic_conds, e);
+    free(e);
+  }
+  pthread_rwlock_unlock(&monotonic_conds_lock);
+
+  return real_pthread_cond_destroy_232(cond);
+}
+
+//where init in pthread methods????
+
+int pthread_cond_timedwait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime, ft_lib_compat_pthread compat)
+{
+  struct timespec tp, tdiff_actual, realtime, faketime;
+  struct timespec *tf = NULL;
+  struct pthread_cond_monotonic* e;
+  char *tmp_env;
+  int wait_ms;
+  clockid_t clk_id;
+  int result = 0;
+
+  if (abstime != NULL)
+  {
+    if (pthread_rwlock_rdlock(&monotonic_conds_lock) != 0) {
+      fprintf(stderr,"can't acquire read monotonic_conds_lock\n");
+      exit(-1);
+    }
+    HASH_FIND_PTR(monotonic_conds, &cond, e);
+    pthread_rwlock_unlock(&monotonic_conds_lock);
+    if (e != NULL)
+      clk_id = CLOCK_MONOTONIC;
+    else
+      clk_id = CLOCK_REALTIME;
+
+    DONT_FAKE_TIME(result = (*real_clock_gettime)(clk_id, &realtime));
+    if (result == -1)
+    {
+      return EINVAL;
+    }
+    faketime = realtime;
+    (void)fake_clock_gettime(clk_id, &faketime);
+
+    if ((tmp_env = getenv("FAKETIME_WAIT_MS")) != NULL)
+    {
+      wait_ms = atol(tmp_env);
+      DONT_FAKE_TIME(result = (*real_clock_gettime)(clk_id, &realtime));
+      if (result == -1)
+      {
+        return EINVAL;
+      }
+
+      tdiff_actual.tv_sec = wait_ms / 1000;
+      tdiff_actual.tv_nsec = (wait_ms % 1000) * 1000000;
+      timespecadd(&realtime, &tdiff_actual, &tp);
+
+      tf = &tp;
+    }
+    else
+    {
+      timespecsub(abstime, &faketime, &tp);
+      if (user_rate_set)
+      {
+        timespecmul(&tp, 1.0 / user_rate, &tdiff_actual);
+      }
+      else
+      {
+        tdiff_actual = tp;
+      }
+    }
+
+    /* For CLOCK_MONOTONIC, pthread_cond_timedwait uses clock_gettime
+       internally to calculate the appropriate duration for the
+       waiting time. This already uses the faked functions, hence, the
+       fake time needs to be passed to pthread_cond_timedwait for
+       CLOCK_MONOTONIC. */
+#ifndef __ARM_ARCH
+    if(clk_id == CLOCK_MONOTONIC)
+      timespecadd(&faketime, &tdiff_actual, &tp);
+    else
+#endif
+      timespecadd(&realtime, &tdiff_actual, &tp);
+
+    tf = &tp;
+  }
+
+  switch (compat) {
+  case FT_COMPAT_GLIBC_2_3_2:
+    result = real_pthread_cond_timedwait_232(cond, mutex, tf);
+    break;
+  case FT_COMPAT_GLIBC_2_2_5:
+    result = real_pthread_cond_timedwait_225(cond, mutex, tf);
+    break;
+  }
+  return result;
+}
+
+int pthread_cond_timedwait_225(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+  return pthread_cond_timedwait_common(cond, mutex, abstime, FT_COMPAT_GLIBC_2_2_5);
+}
+
+int pthread_cond_timedwait_232(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+  return pthread_cond_timedwait_common(cond, mutex, abstime, FT_COMPAT_GLIBC_2_3_2);
+}
+
+__asm__(".symver pthread_cond_timedwait_225, pthread_cond_timedwait@GLIBC_2.2.5");
+#ifdef __ARM_ARCH
+__asm__(".symver pthread_cond_timedwait_232, pthread_cond_timedwait@@");
+__asm__(".symver pthread_cond_init_232, pthread_cond_init@@");
+__asm__(".symver pthread_cond_destroy_232, pthread_cond_destroy@@");
+#else
+__asm__(".symver pthread_cond_timedwait_232, pthread_cond_timedwait@@GLIBC_2.3.2");
+__asm__(".symver pthread_cond_init_232, pthread_cond_init@@GLIBC_2.3.2");
+__asm__(".symver pthread_cond_destroy_232, pthread_cond_destroy@@GLIBC_2.3.2");
+#endif
+
+#endif
+
+
 /*
  * Editor modelines
  *
diff --git a/src/libfaketime.map b/src/libfaketime.map
index 6e008cf..eb873e7 100644
--- a/src/libfaketime.map
+++ b/src/libfaketime.map
@@ -4,7 +4,18 @@ GLIBC_2.2 {
   timer_gettime; timer_settime;
   local: timer_settime_*; timer_gettime_*;
 };
+
 GLIBC_2.3.3 {
   # Changed timer_t.
   timer_gettime; timer_settime;
 } GLIBC_2.2;
+
+GLIBC_2.2.5 {
+  global:  pthread_cond_timedwait;
+  local: pthread_cond_timedwait_*; pthread_cond_init_*; pthread_cond_destroy*;
+};
+
+GLIBC_2.3.2 {
+  pthread_cond_timedwait; pthread_cond_init; pthread_cond_destroy;
+} GLIBC_2.2.5;
+
diff --git a/src/uthash.h b/src/uthash.h
new file mode 100644
index 0000000..7e64cac
--- /dev/null
+++ b/src/uthash.h
@@ -0,0 +1,1208 @@
+/*
+Copyright (c) 2003-2017, Troy D. Hanson     http://troydhanson.github.com/uthash/
+All rights reserved.
+
+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.
+
+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.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#define UTHASH_VERSION 2.0.2
+
+#include <string.h>   /* memcmp, memset, strlen */
+#include <stddef.h>   /* ptrdiff_t */
+#include <stdlib.h>   /* exit */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ source) this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)
+#if defined(_MSC_VER)   /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else                   /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#endif
+#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#else                   /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE(x)
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  char **_da_dst = (char**)(&(dst));                                             \
+  *_da_dst = (char*)(src);                                                       \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  (dst) = DECLTYPE(dst)(src);                                                    \
+} while (0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined(_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include <stdint.h>
+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#elif defined(__GNUC__) && !defined(__VXWORKS__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
+#endif
+#ifndef uthash_bzero
+#define uthash_bzero(a,n) memset(a,'\0',n)
+#endif
+#ifndef uthash_memcmp
+#define uthash_memcmp(a,b,n) memcmp(a,b,n)
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
+#endif
+
+#ifndef HASH_NONFATAL_OOM
+#define HASH_NONFATAL_OOM 0
+#endif
+
+#if HASH_NONFATAL_OOM
+/* malloc failures can be recovered from */
+
+#ifndef uthash_nonfatal_oom
+#define uthash_nonfatal_oom(obj) do {} while (0)    /* non-fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)
+#define IF_HASH_NONFATAL_OOM(x) x
+
+#else
+/* malloc failures result in lost memory, hash tables are unusable */
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1)        /* fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory")
+#define IF_HASH_NONFATAL_OOM(x)
+
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_ROLLBACK_BKT(hh, head, itemptrhh)                                   \
+do {                                                                             \
+  struct UT_hash_handle *_hd_hh_item = (itemptrhh);                              \
+  unsigned _hd_bkt;                                                              \
+  HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);         \
+  (head)->hh.tbl->buckets[_hd_bkt].count++;                                      \
+  _hd_hh_item->hh_next = NULL;                                                   \
+  _hd_hh_item->hh_prev = NULL;                                                   \
+} while (0)
+
+#define HASH_VALUE(keyptr,keylen,hashv)                                          \
+do {                                                                             \
+  HASH_FCN(keyptr, keylen, hashv);                                               \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \
+do {                                                                             \
+  (out) = NULL;                                                                  \
+  if (head) {                                                                    \
+    unsigned _hf_bkt;                                                            \
+    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \
+    if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) {                         \
+      HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
+do {                                                                             \
+  unsigned _hf_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen, _hf_hashv);                                         \
+  HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);               \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl,oomed)                                               \
+do {                                                                             \
+  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
+  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
+  if (!(tbl)->bloom_bv) {                                                        \
+    HASH_RECORD_OOM(oomed);                                                      \
+  } else {                                                                       \
+    uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                           \
+    (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                     \
+  }                                                                              \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl)                                                     \
+do {                                                                             \
+  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv)                                                \
+  HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv)                                               \
+  HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl,oomed)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head,oomed)                                           \
+do {                                                                             \
+  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table));         \
+  if (!(head)->hh.tbl) {                                                         \
+    HASH_RECORD_OOM(oomed);                                                      \
+  } else {                                                                       \
+    uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table));                         \
+    (head)->hh.tbl->tail = &((head)->hh);                                        \
+    (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                      \
+    (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;            \
+    (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                  \
+    (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                    \
+        HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));               \
+    (head)->hh.tbl->signature = HASH_SIGNATURE;                                  \
+    if (!(head)->hh.tbl->buckets) {                                              \
+      HASH_RECORD_OOM(oomed);                                                    \
+      uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                        \
+    } else {                                                                     \
+      uthash_bzero((head)->hh.tbl->buckets,                                      \
+          HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));             \
+      HASH_BLOOM_MAKE((head)->hh.tbl, oomed);                                    \
+      IF_HASH_NONFATAL_OOM(                                                      \
+        if (oomed) {                                                             \
+          uthash_free((head)->hh.tbl->buckets,                                   \
+              HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));           \
+          uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                    \
+        }                                                                        \
+      )                                                                          \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do {                                                                             \
+  (replaced) = NULL;                                                             \
+  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+  if (replaced) {                                                                \
+    HASH_DELETE(hh, head, replaced);                                             \
+  }                                                                              \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do {                                                                             \
+  (replaced) = NULL;                                                             \
+  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+  if (replaced) {                                                                \
+    HASH_DELETE(hh, head, replaced);                                             \
+  }                                                                              \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
+do {                                                                             \
+  unsigned _hr_hashv;                                                            \
+  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
+  HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \
+do {                                                                             \
+  unsigned _hr_hashv;                                                            \
+  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
+  HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add)                                          \
+do {                                                                             \
+  (add)->hh.next = NULL;                                                         \
+  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \
+  (head)->hh.tbl->tail->next = (add);                                            \
+  (head)->hh.tbl->tail = &((add)->hh);                                           \
+} while (0)
+
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \
+do {                                                                             \
+  do {                                                                           \
+    if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) {                             \
+      break;                                                                     \
+    }                                                                            \
+  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \
+} while (0)
+
+#ifdef NO_DECLTYPE
+#undef HASH_AKBI_INNER_LOOP
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \
+do {                                                                             \
+  char *_hs_saved_head = (char*)(head);                                          \
+  do {                                                                           \
+    DECLTYPE_ASSIGN(head, _hs_iter);                                             \
+    if (cmpfcn(head, add) > 0) {                                                 \
+      DECLTYPE_ASSIGN(head, _hs_saved_head);                                     \
+      break;                                                                     \
+    }                                                                            \
+    DECLTYPE_ASSIGN(head, _hs_saved_head);                                       \
+  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \
+} while (0)
+#endif
+
+#if HASH_NONFATAL_OOM
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \
+do {                                                                             \
+  if (!(oomed)) {                                                                \
+    unsigned _ha_bkt;                                                            \
+    (head)->hh.tbl->num_items++;                                                 \
+    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                  \
+    HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);    \
+    if (oomed) {                                                                 \
+      HASH_ROLLBACK_BKT(hh, head, &(add)->hh);                                   \
+      HASH_DELETE_HH(hh, head, &(add)->hh);                                      \
+      (add)->hh.tbl = NULL;                                                      \
+      uthash_nonfatal_oom(add);                                                  \
+    } else {                                                                     \
+      HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                   \
+      HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                \
+    }                                                                            \
+  } else {                                                                       \
+    (add)->hh.tbl = NULL;                                                        \
+    uthash_nonfatal_oom(add);                                                    \
+  }                                                                              \
+} while (0)
+
+#else
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \
+do {                                                                             \
+  unsigned _ha_bkt;                                                              \
+  (head)->hh.tbl->num_items++;                                                   \
+  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
+  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);      \
+  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
+  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
+} while (0)
+
+#endif
+
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do {                                                                             \
+  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \
+  (add)->hh.hashv = (hashval);                                                   \
+  (add)->hh.key = (char*) (keyptr);                                              \
+  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
+  if (!(head)) {                                                                 \
+    (add)->hh.next = NULL;                                                       \
+    (add)->hh.prev = NULL;                                                       \
+    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \
+    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \
+      (head) = (add);                                                            \
+    IF_HASH_NONFATAL_OOM( } )                                                    \
+  } else {                                                                       \
+    void *_hs_iter = (head);                                                     \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn);                                 \
+    if (_hs_iter) {                                                              \
+      (add)->hh.next = _hs_iter;                                                 \
+      if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) {     \
+        HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add);              \
+      } else {                                                                   \
+        (head) = (add);                                                          \
+      }                                                                          \
+      HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add);                      \
+    } else {                                                                     \
+      HASH_APPEND_LIST(hh, head, add);                                           \
+    }                                                                            \
+  }                                                                              \
+  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \
+  HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER");                    \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \
+do {                                                                             \
+  unsigned _hs_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \
+  HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \
+do {                                                                             \
+  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \
+  (add)->hh.hashv = (hashval);                                                   \
+  (add)->hh.key = (char*) (keyptr);                                              \
+  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
+  if (!(head)) {                                                                 \
+    (add)->hh.next = NULL;                                                       \
+    (add)->hh.prev = NULL;                                                       \
+    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \
+    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \
+      (head) = (add);                                                            \
+    IF_HASH_NONFATAL_OOM( } )                                                    \
+  } else {                                                                       \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    HASH_APPEND_LIST(hh, head, add);                                             \
+  }                                                                              \
+  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \
+  HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE");                            \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+do {                                                                             \
+  unsigned _ha_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
+  HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \
+do {                                                                             \
+  bkt = ((hashv) & ((num_bkts) - 1U));                                           \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ *  HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr)                                              \
+    HASH_DELETE_HH(hh, head, &(delptr)->hh)
+
+#define HASH_DELETE_HH(hh,head,delptrhh)                                         \
+do {                                                                             \
+  struct UT_hash_handle *_hd_hh_del = (delptrhh);                                \
+  if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) {                \
+    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
+    uthash_free((head)->hh.tbl->buckets,                                         \
+                (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket));    \
+    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
+    (head) = NULL;                                                               \
+  } else {                                                                       \
+    unsigned _hd_bkt;                                                            \
+    if (_hd_hh_del == (head)->hh.tbl->tail) {                                    \
+      (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev);     \
+    }                                                                            \
+    if (_hd_hh_del->prev != NULL) {                                              \
+      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next;   \
+    } else {                                                                     \
+      DECLTYPE_ASSIGN(head, _hd_hh_del->next);                                   \
+    }                                                                            \
+    if (_hd_hh_del->next != NULL) {                                              \
+      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev;   \
+    }                                                                            \
+    HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);        \
+    HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);               \
+    (head)->hh.tbl->num_items--;                                                 \
+  }                                                                              \
+  HASH_FSCK(hh, head, "HASH_DELETE_HH");                                         \
+} while (0)
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out)                                          \
+    HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add)                                          \
+    HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add)
+#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced)
+#define HASH_FIND_INT(head,findint,out)                                          \
+    HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add)                                          \
+    HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out)                                          \
+    HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add)                                          \
+    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr)                                                    \
+    HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head,where)                                                 \
+do {                                                                             \
+  struct UT_hash_handle *_thh;                                                   \
+  if (head) {                                                                    \
+    unsigned _bkt_i;                                                             \
+    unsigned _count = 0;                                                         \
+    char *_prev;                                                                 \
+    for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) {           \
+      unsigned _bkt_count = 0;                                                   \
+      _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                            \
+      _prev = NULL;                                                              \
+      while (_thh) {                                                             \
+        if (_prev != (char*)(_thh->hh_prev)) {                                   \
+          HASH_OOPS("%s: invalid hh_prev %p, actual %p\n",                       \
+              (where), (void*)_thh->hh_prev, (void*)_prev);                      \
+        }                                                                        \
+        _bkt_count++;                                                            \
+        _prev = (char*)(_thh);                                                   \
+        _thh = _thh->hh_next;                                                    \
+      }                                                                          \
+      _count += _bkt_count;                                                      \
+      if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {                \
+        HASH_OOPS("%s: invalid bucket count %u, actual %u\n",                    \
+            (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);         \
+      }                                                                          \
+    }                                                                            \
+    if (_count != (head)->hh.tbl->num_items) {                                   \
+      HASH_OOPS("%s: invalid hh item count %u, actual %u\n",                     \
+          (where), (head)->hh.tbl->num_items, _count);                           \
+    }                                                                            \
+    _count = 0;                                                                  \
+    _prev = NULL;                                                                \
+    _thh =  &(head)->hh;                                                         \
+    while (_thh) {                                                               \
+      _count++;                                                                  \
+      if (_prev != (char*)_thh->prev) {                                          \
+        HASH_OOPS("%s: invalid prev %p, actual %p\n",                            \
+            (where), (void*)_thh->prev, (void*)_prev);                           \
+      }                                                                          \
+      _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                         \
+      _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL);     \
+    }                                                                            \
+    if (_count != (head)->hh.tbl->num_items) {                                   \
+      HASH_OOPS("%s: invalid app item count %u, actual %u\n",                    \
+          (where), (head)->hh.tbl->num_items, _count);                           \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+#else
+#define HASH_FSCK(hh,head,where)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
+do {                                                                             \
+  unsigned _klen = fieldlen;                                                     \
+  write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                  \
+  write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                        \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _hb_keylen = (unsigned)keylen;                                        \
+  const unsigned char *_hb_key = (const unsigned char*)(key);                    \
+  (hashv) = 0;                                                                   \
+  while (_hb_keylen-- != 0U) {                                                   \
+    (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++;                           \
+  }                                                                              \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _sx_i;                                                                \
+  const unsigned char *_hs_key = (const unsigned char*)(key);                    \
+  hashv = 0;                                                                     \
+  for (_sx_i=0; _sx_i < keylen; _sx_i++) {                                       \
+    hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                       \
+  }                                                                              \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _fn_i;                                                                \
+  const unsigned char *_hf_key = (const unsigned char*)(key);                    \
+  (hashv) = 2166136261U;                                                         \
+  for (_fn_i=0; _fn_i < keylen; _fn_i++) {                                       \
+    hashv = hashv ^ _hf_key[_fn_i];                                              \
+    hashv = hashv * 16777619U;                                                   \
+  }                                                                              \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _ho_i;                                                                \
+  const unsigned char *_ho_key=(const unsigned char*)(key);                      \
+  hashv = 0;                                                                     \
+  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \
+      hashv += _ho_key[_ho_i];                                                   \
+      hashv += (hashv << 10);                                                    \
+      hashv ^= (hashv >> 6);                                                     \
+  }                                                                              \
+  hashv += (hashv << 3);                                                         \
+  hashv ^= (hashv >> 11);                                                        \
+  hashv += (hashv << 15);                                                        \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c)                                                      \
+do {                                                                             \
+  a -= b; a -= c; a ^= ( c >> 13 );                                              \
+  b -= c; b -= a; b ^= ( a << 8 );                                               \
+  c -= a; c -= b; c ^= ( b >> 13 );                                              \
+  a -= b; a -= c; a ^= ( c >> 12 );                                              \
+  b -= c; b -= a; b ^= ( a << 16 );                                              \
+  c -= a; c -= b; c ^= ( b >> 5 );                                               \
+  a -= b; a -= c; a ^= ( c >> 3 );                                               \
+  b -= c; b -= a; b ^= ( a << 10 );                                              \
+  c -= a; c -= b; c ^= ( b >> 15 );                                              \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _hj_i,_hj_j,_hj_k;                                                    \
+  unsigned const char *_hj_key=(unsigned const char*)(key);                      \
+  hashv = 0xfeedbeefu;                                                           \
+  _hj_i = _hj_j = 0x9e3779b9u;                                                   \
+  _hj_k = (unsigned)(keylen);                                                    \
+  while (_hj_k >= 12U) {                                                         \
+    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \
+        + ( (unsigned)_hj_key[2] << 16 )                                         \
+        + ( (unsigned)_hj_key[3] << 24 ) );                                      \
+    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \
+        + ( (unsigned)_hj_key[6] << 16 )                                         \
+        + ( (unsigned)_hj_key[7] << 24 ) );                                      \
+    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \
+        + ( (unsigned)_hj_key[10] << 16 )                                        \
+        + ( (unsigned)_hj_key[11] << 24 ) );                                     \
+                                                                                 \
+     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
+                                                                                 \
+     _hj_key += 12;                                                              \
+     _hj_k -= 12U;                                                               \
+  }                                                                              \
+  hashv += (unsigned)(keylen);                                                   \
+  switch ( _hj_k ) {                                                             \
+    case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */         \
+    case 10: hashv += ( (unsigned)_hj_key[9] << 16 );  /* FALLTHROUGH */         \
+    case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );   /* FALLTHROUGH */         \
+    case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );  /* FALLTHROUGH */         \
+    case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );  /* FALLTHROUGH */         \
+    case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );   /* FALLTHROUGH */         \
+    case 5:  _hj_j += _hj_key[4];                      /* FALLTHROUGH */         \
+    case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );  /* FALLTHROUGH */         \
+    case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );  /* FALLTHROUGH */         \
+    case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );   /* FALLTHROUGH */         \
+    case 1:  _hj_i += _hj_key[0];                                                \
+  }                                                                              \
+  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
+  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
+                       +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned const char *_sfh_key=(unsigned const char*)(key);                     \
+  uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \
+                                                                                 \
+  unsigned _sfh_rem = _sfh_len & 3U;                                             \
+  _sfh_len >>= 2;                                                                \
+  hashv = 0xcafebabeu;                                                           \
+                                                                                 \
+  /* Main loop */                                                                \
+  for (;_sfh_len > 0U; _sfh_len--) {                                             \
+    hashv    += get16bits (_sfh_key);                                            \
+    _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv;              \
+    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \
+    _sfh_key += 2U*sizeof (uint16_t);                                            \
+    hashv    += hashv >> 11;                                                     \
+  }                                                                              \
+                                                                                 \
+  /* Handle end cases */                                                         \
+  switch (_sfh_rem) {                                                            \
+    case 3: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 16;                                                \
+            hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18;              \
+            hashv += hashv >> 11;                                                \
+            break;                                                               \
+    case 2: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 11;                                                \
+            hashv += hashv >> 17;                                                \
+            break;                                                               \
+    case 1: hashv += *_sfh_key;                                                  \
+            hashv ^= hashv << 10;                                                \
+            hashv += hashv >> 1;                                                 \
+  }                                                                              \
+                                                                                 \
+  /* Force "avalanching" of final 127 bits */                                    \
+  hashv ^= hashv << 3;                                                           \
+  hashv += hashv >> 5;                                                           \
+  hashv ^= hashv << 4;                                                           \
+  hashv += hashv >> 17;                                                          \
+  hashv ^= hashv << 25;                                                          \
+  hashv += hashv >> 6;                                                           \
+} while (0)
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * MurmurHash uses the faster approach only on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ *   gcc -m64 -dM -E - < /dev/null                  (on gcc)
+ *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__)  || defined(_M_IX86))
+#define MUR_GETBLOCK(p,i) p[i]
+#else /* non intel */
+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
+#define MUR_TWO_TWO(p)   ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >>  8))
+#else /* assume little endian non-intel */
+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
+#define MUR_TWO_TWO(p)   ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) <<  8))
+#endif
+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) :           \
+                            (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
+                             (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) :  \
+                                                      MUR_ONE_THREE(p))))
+#endif
+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define MUR_FMIX(_h) \
+do {                 \
+  _h ^= _h >> 16;    \
+  _h *= 0x85ebca6bu; \
+  _h ^= _h >> 13;    \
+  _h *= 0xc2b2ae35u; \
+  _h ^= _h >> 16;    \
+} while (0)
+
+#define HASH_MUR(key,keylen,hashv)                                     \
+do {                                                                   \
+  const uint8_t *_mur_data = (const uint8_t*)(key);                    \
+  const int _mur_nblocks = (int)(keylen) / 4;                          \
+  uint32_t _mur_h1 = 0xf88D5353u;                                      \
+  uint32_t _mur_c1 = 0xcc9e2d51u;                                      \
+  uint32_t _mur_c2 = 0x1b873593u;                                      \
+  uint32_t _mur_k1 = 0;                                                \
+  const uint8_t *_mur_tail;                                            \
+  const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \
+  int _mur_i;                                                          \
+  for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) {                \
+    _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i);                        \
+    _mur_k1 *= _mur_c1;                                                \
+    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
+    _mur_k1 *= _mur_c2;                                                \
+                                                                       \
+    _mur_h1 ^= _mur_k1;                                                \
+    _mur_h1 = MUR_ROTL32(_mur_h1,13);                                  \
+    _mur_h1 = (_mur_h1*5U) + 0xe6546b64u;                              \
+  }                                                                    \
+  _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4));          \
+  _mur_k1=0;                                                           \
+  switch ((keylen) & 3U) {                                             \
+    case 0: break;                                                     \
+    case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \
+    case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8;  /* FALLTHROUGH */ \
+    case 1: _mur_k1 ^= (uint32_t)_mur_tail[0];                         \
+    _mur_k1 *= _mur_c1;                                                \
+    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
+    _mur_k1 *= _mur_c2;                                                \
+    _mur_h1 ^= _mur_k1;                                                \
+  }                                                                    \
+  _mur_h1 ^= (uint32_t)(keylen);                                       \
+  MUR_FMIX(_mur_h1);                                                   \
+  hashv = _mur_h1;                                                     \
+} while (0)
+#endif  /* HASH_USING_NO_STRICT_ALIASING */
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \
+do {                                                                             \
+  if ((head).hh_head != NULL) {                                                  \
+    DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \
+  } else {                                                                       \
+    (out) = NULL;                                                                \
+  }                                                                              \
+  while ((out) != NULL) {                                                        \
+    if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \
+      if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) {                \
+        break;                                                                   \
+      }                                                                          \
+    }                                                                            \
+    if ((out)->hh.hh_next != NULL) {                                             \
+      DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \
+    } else {                                                                     \
+      (out) = NULL;                                                              \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+/* add an item to a bucket  */
+#define HASH_ADD_TO_BKT(head,hh,addhh,oomed)                                     \
+do {                                                                             \
+  UT_hash_bucket *_ha_head = &(head);                                            \
+  _ha_head->count++;                                                             \
+  (addhh)->hh_next = _ha_head->hh_head;                                          \
+  (addhh)->hh_prev = NULL;                                                       \
+  if (_ha_head->hh_head != NULL) {                                               \
+    _ha_head->hh_head->hh_prev = (addhh);                                        \
+  }                                                                              \
+  _ha_head->hh_head = (addhh);                                                   \
+  if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \
+      && !(addhh)->tbl->noexpand) {                                              \
+    HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed);                              \
+    IF_HASH_NONFATAL_OOM(                                                        \
+      if (oomed) {                                                               \
+        HASH_DEL_IN_BKT(head,addhh);                                             \
+      }                                                                          \
+    )                                                                            \
+  }                                                                              \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(head,delhh)                                              \
+do {                                                                             \
+  UT_hash_bucket *_hd_head = &(head);                                            \
+  _hd_head->count--;                                                             \
+  if (_hd_head->hh_head == (delhh)) {                                            \
+    _hd_head->hh_head = (delhh)->hh_next;                                        \
+  }                                                                              \
+  if ((delhh)->hh_prev) {                                                        \
+    (delhh)->hh_prev->hh_next = (delhh)->hh_next;                                \
+  }                                                                              \
+  if ((delhh)->hh_next) {                                                        \
+    (delhh)->hh_next->hh_prev = (delhh)->hh_prev;                                \
+  }                                                                              \
+} while (0)
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ *      ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(hh,tbl,oomed)                                        \
+do {                                                                             \
+  unsigned _he_bkt;                                                              \
+  unsigned _he_bkt_i;                                                            \
+  struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                   \
+  UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                  \
+  _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                              \
+           2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket));            \
+  if (!_he_new_buckets) {                                                        \
+    HASH_RECORD_OOM(oomed);                                                      \
+  } else {                                                                       \
+    uthash_bzero(_he_new_buckets,                                                \
+        2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket));               \
+    (tbl)->ideal_chain_maxlen =                                                  \
+       ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) +                      \
+       ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);    \
+    (tbl)->nonideal_items = 0;                                                   \
+    for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) {           \
+      _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head;                             \
+      while (_he_thh != NULL) {                                                  \
+        _he_hh_nxt = _he_thh->hh_next;                                           \
+        HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt);           \
+        _he_newbkt = &(_he_new_buckets[_he_bkt]);                                \
+        if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) {                 \
+          (tbl)->nonideal_items++;                                               \
+          _he_newbkt->expand_mult = _he_newbkt->count / (tbl)->ideal_chain_maxlen; \
+        }                                                                        \
+        _he_thh->hh_prev = NULL;                                                 \
+        _he_thh->hh_next = _he_newbkt->hh_head;                                  \
+        if (_he_newbkt->hh_head != NULL) {                                       \
+          _he_newbkt->hh_head->hh_prev = _he_thh;                                \
+        }                                                                        \
+        _he_newbkt->hh_head = _he_thh;                                           \
+        _he_thh = _he_hh_nxt;                                                    \
+      }                                                                          \
+    }                                                                            \
+    uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+    (tbl)->num_buckets *= 2U;                                                    \
+    (tbl)->log2_num_buckets++;                                                   \
+    (tbl)->buckets = _he_new_buckets;                                            \
+    (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ?   \
+        ((tbl)->ineff_expands+1U) : 0U;                                          \
+    if ((tbl)->ineff_expands > 1U) {                                             \
+      (tbl)->noexpand = 1;                                                       \
+      uthash_noexpand_fyi(tbl);                                                  \
+    }                                                                            \
+    uthash_expand_fyi(tbl);                                                      \
+  }                                                                              \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn)                                                 \
+do {                                                                             \
+  unsigned _hs_i;                                                                \
+  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
+  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
+  if (head != NULL) {                                                            \
+    _hs_insize = 1;                                                              \
+    _hs_looping = 1;                                                             \
+    _hs_list = &((head)->hh);                                                    \
+    while (_hs_looping != 0U) {                                                  \
+      _hs_p = _hs_list;                                                          \
+      _hs_list = NULL;                                                           \
+      _hs_tail = NULL;                                                           \
+      _hs_nmerges = 0;                                                           \
+      while (_hs_p != NULL) {                                                    \
+        _hs_nmerges++;                                                           \
+        _hs_q = _hs_p;                                                           \
+        _hs_psize = 0;                                                           \
+        for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) {                           \
+          _hs_psize++;                                                           \
+          _hs_q = ((_hs_q->next != NULL) ?                                       \
+            HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                   \
+          if (_hs_q == NULL) {                                                   \
+            break;                                                               \
+          }                                                                      \
+        }                                                                        \
+        _hs_qsize = _hs_insize;                                                  \
+        while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) {    \
+          if (_hs_psize == 0U) {                                                 \
+            _hs_e = _hs_q;                                                       \
+            _hs_q = ((_hs_q->next != NULL) ?                                     \
+              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \
+            _hs_qsize--;                                                         \
+          } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) {                     \
+            _hs_e = _hs_p;                                                       \
+            if (_hs_p != NULL) {                                                 \
+              _hs_p = ((_hs_p->next != NULL) ?                                   \
+                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \
+            }                                                                    \
+            _hs_psize--;                                                         \
+          } else if ((cmpfcn(                                                    \
+                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)),             \
+                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q))              \
+                )) <= 0) {                                                       \
+            _hs_e = _hs_p;                                                       \
+            if (_hs_p != NULL) {                                                 \
+              _hs_p = ((_hs_p->next != NULL) ?                                   \
+                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \
+            }                                                                    \
+            _hs_psize--;                                                         \
+          } else {                                                               \
+            _hs_e = _hs_q;                                                       \
+            _hs_q = ((_hs_q->next != NULL) ?                                     \
+              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \
+            _hs_qsize--;                                                         \
+          }                                                                      \
+          if ( _hs_tail != NULL ) {                                              \
+            _hs_tail->next = ((_hs_e != NULL) ?                                  \
+              ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL);                       \
+          } else {                                                               \
+            _hs_list = _hs_e;                                                    \
+          }                                                                      \
+          if (_hs_e != NULL) {                                                   \
+            _hs_e->prev = ((_hs_tail != NULL) ?                                  \
+              ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL);                    \
+          }                                                                      \
+          _hs_tail = _hs_e;                                                      \
+        }                                                                        \
+        _hs_p = _hs_q;                                                           \
+      }                                                                          \
+      if (_hs_tail != NULL) {                                                    \
+        _hs_tail->next = NULL;                                                   \
+      }                                                                          \
+      if (_hs_nmerges <= 1U) {                                                   \
+        _hs_looping = 0;                                                         \
+        (head)->hh.tbl->tail = _hs_tail;                                         \
+        DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list));           \
+      }                                                                          \
+      _hs_insize *= 2U;                                                          \
+    }                                                                            \
+    HASH_FSCK(hh, head, "HASH_SRT");                                             \
+  }                                                                              \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
+do {                                                                             \
+  unsigned _src_bkt, _dst_bkt;                                                   \
+  void *_last_elt = NULL, *_elt;                                                 \
+  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
+  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
+  if ((src) != NULL) {                                                           \
+    for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {    \
+      for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;               \
+        _src_hh != NULL;                                                         \
+        _src_hh = _src_hh->hh_next) {                                            \
+        _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                         \
+        if (cond(_elt)) {                                                        \
+          IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; )                             \
+          _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);                 \
+          _dst_hh->key = _src_hh->key;                                           \
+          _dst_hh->keylen = _src_hh->keylen;                                     \
+          _dst_hh->hashv = _src_hh->hashv;                                       \
+          _dst_hh->prev = _last_elt;                                             \
+          _dst_hh->next = NULL;                                                  \
+          if (_last_elt_hh != NULL) {                                            \
+            _last_elt_hh->next = _elt;                                           \
+          }                                                                      \
+          if ((dst) == NULL) {                                                   \
+            DECLTYPE_ASSIGN(dst, _elt);                                          \
+            HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed);                             \
+            IF_HASH_NONFATAL_OOM(                                                \
+              if (_hs_oomed) {                                                   \
+                uthash_nonfatal_oom(_elt);                                       \
+                (dst) = NULL;                                                    \
+                continue;                                                        \
+              }                                                                  \
+            )                                                                    \
+          } else {                                                               \
+            _dst_hh->tbl = (dst)->hh_dst.tbl;                                    \
+          }                                                                      \
+          HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);      \
+          HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \
+          (dst)->hh_dst.tbl->num_items++;                                        \
+          IF_HASH_NONFATAL_OOM(                                                  \
+            if (_hs_oomed) {                                                     \
+              HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh);                           \
+              HASH_DELETE_HH(hh_dst, dst, _dst_hh);                              \
+              _dst_hh->tbl = NULL;                                               \
+              uthash_nonfatal_oom(_elt);                                         \
+              continue;                                                          \
+            }                                                                    \
+          )                                                                      \
+          HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv);                          \
+          _last_elt = _elt;                                                      \
+          _last_elt_hh = _dst_hh;                                                \
+        }                                                                        \
+      }                                                                          \
+    }                                                                            \
+  }                                                                              \
+  HASH_FSCK(hh_dst, dst, "HASH_SELECT");                                         \
+} while (0)
+
+#define HASH_CLEAR(hh,head)                                                      \
+do {                                                                             \
+  if ((head) != NULL) {                                                          \
+    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
+    uthash_free((head)->hh.tbl->buckets,                                         \
+                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
+    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
+    (head) = NULL;                                                               \
+  }                                                                              \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head)                                                   \
+ (((head) != NULL) ? (                                                           \
+ (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \
+          ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \
+           sizeof(UT_hash_table)                                   +             \
+           (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+  (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \
+  (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+   struct UT_hash_handle *hh_head;
+   unsigned count;
+
+   /* expand_mult is normally set to 0. In this situation, the max chain length
+    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+    * the bucket's chain exceeds this length, bucket expansion is triggered).
+    * However, setting expand_mult to a non-zero value delays bucket expansion
+    * (that would be triggered by additions to this particular bucket)
+    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+    * (The multiplier is simply expand_mult+1). The whole idea of this
+    * multiplier is to reduce bucket expansions, since they are expensive, in
+    * situations where we know that a particular bucket tends to be overused.
+    * It is better to let its chain length grow to a longer yet-still-bounded
+    * value, than to do an O(n) bucket expansion too often.
+    */
+   unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+   UT_hash_bucket *buckets;
+   unsigned num_buckets, log2_num_buckets;
+   unsigned num_items;
+   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
+   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+   /* in an ideal situation (all buckets used equally), no bucket would have
+    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+   unsigned ideal_chain_maxlen;
+
+   /* nonideal_items is the number of items in the hash whose chain position
+    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+    * hash distribution; reaching them in a chain traversal takes >ideal steps */
+   unsigned nonideal_items;
+
+   /* ineffective expands occur when a bucket doubling was performed, but
+    * afterward, more than half the items in the hash had nonideal chain
+    * positions. If this happens on two consecutive expansions we inhibit any
+    * further expansion, as it's not helping; this happens when the hash
+    * function isn't a good fit for the key domain. When expansion is inhibited
+    * the hash will still work, albeit no longer in constant time. */
+   unsigned ineff_expands, noexpand;
+
+   uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+   uint8_t *bloom_bv;
+   uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+   struct UT_hash_table *tbl;
+   void *prev;                       /* prev element in app order      */
+   void *next;                       /* next element in app order      */
+   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
+   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
+   void *key;                        /* ptr to enclosing struct's key  */
+   unsigned keylen;                  /* enclosing struct's key len     */
+   unsigned hashv;                   /* result of hash-fcn(key)        */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/test/Makefile b/test/Makefile
index f2d2f92..94d1a2e 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,7 +1,7 @@
 CC = gcc
 
 CFLAGS = -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra
-LDFLAGS = -lrt
+LDFLAGS = -lrt -lpthread
 
 SRC = timetest.c
 OBJ = ${SRC:.c=.o}
diff --git a/test/README-testframe.txt b/test/README-testframe.txt
index f05ee85..d6980c3 100644
--- a/test/README-testframe.txt
+++ b/test/README-testframe.txt
@@ -35,5 +35,5 @@
 # 3. create DIR/test_XXX.sh.
 # 4. write a run function and testcase functions in DIR/test_XXX.sh.
 # 5. within the run function, call run_testcase for each testcase function.
-# 6. within each testcase funtion, call assertneq or asserteq, or do
+# 6. within each testcase function, call assertneq or asserteq, or do
 #    the equivalent.
diff --git a/test/functests/test_exclude_mono.sh b/test/functests/test_exclude_mono.sh
index f68fc20..1d3ec1e 100644
--- a/test/functests/test_exclude_mono.sh
+++ b/test/functests/test_exclude_mono.sh
@@ -1,12 +1,12 @@
-# Checks that setting DONT_FAKE_MONOTONIC actually prevent
+# Checks that setting FAKETIME_DONT_FAKE_MONOTONIC actually prevent
 # libfaketime from faking monotonic clocks.
 #
 # We do this by freezing time at a specific and arbitrary date with faketime,
-# and making sure that if we set DONT_FAKE_MONOTONIC to 1, calling
+# and making sure that if we set FAKETIME_DONT_FAKE_MONOTONIC to 1, calling
 # clock_gettime(CLOCK_MONOTONIC) returns two different values.
 #
-# We also make sure that if we don't set DONT_FAKE_MONOTONIC to 1, in other
-# words when we use the default behavior, two subsequent calls to
+# We also make sure that if we don't set FAKETIME_DONT_FAKE_MONOTONIC to 1,
+# in other words when we use the default behavior, two subsequent calls to
 # clock_gettime(CLOCK_MONOTONIC) do return different values.
 
 init()
@@ -26,7 +26,7 @@ run()
     init
 
     run_testcase dont_fake_mono
-    run_testcase fake_mono
+    # run_testcase fake_mono
 }
 
 get_token()
@@ -64,7 +64,8 @@ get_monotonic_time()
 {
     dont_fake_mono=$1; shift;
     clock_id=$1; shift;
-    DONT_FAKE_MONOTONIC=${dont_fake_mono} fakecmd "2014-07-21 09:00:00" \
+    FAKETIME_DONT_FAKE_MONOTONIC=${dont_fake_mono} \
+    fakecmd "2014-07-21 09:00:00" \
     /bin/bash -c "for i in 1 2; do \
     perl -w -MTime::HiRes=clock_gettime,${clock_id} -E \
     'say clock_gettime(${clock_id})'; \
diff --git a/test/functests/test_true.sh b/test/functests/test_true.sh
index 161452f..0025bcf 100644
--- a/test/functests/test_true.sh
+++ b/test/functests/test_true.sh
@@ -1,4 +1,4 @@
-# test suite that always succeds - for testing framework
+# test suite that always succeeds - for testing framework
 
 run()
 {
diff --git a/test/functests/test_walkone.sh b/test/functests/test_walkone.sh
index b27df9a..20efd9f 100755
--- a/test/functests/test_walkone.sh
+++ b/test/functests/test_walkone.sh
@@ -37,7 +37,15 @@ fakedate()
 	#
 	typeset fmt='%s'
 	export FAKETIME_FMT=$fmt
-	fakecmd "$1" date +$fmt
+	if [ "mac" == "$PLATFORM" ]; then
+    if [ -x /usr/local/bin/gdate ] ; then
+      	fakecmd "$1" gdate +$fmt
+      else
+        echo "<skip>"
+      fi
+  else
+  	fakecmd "$1" date +$fmt
+  fi
 }
 
 #
@@ -63,5 +71,14 @@ test_with_i()
 	typeset i="$1"
 	typeset t=$(pow 2 $i)
 
-	asserteq $(fakedate $t) $t "(secs since Epoch)"
+  if [ "mac" == "$PLATFORM" ]; then
+    if [ -x /usr/local/bin/gdate ] ; then
+	    asserteq $(fakedate $t) $t "(secs since Epoch)"
+    else
+      asserteq $t $t "(skipping test, install gdate)"
+    fi
+
+  else
+	  asserteq $(fakedate $t) $t "(secs since Epoch)"
+  fi
 }
diff --git a/test/test.sh b/test/test.sh
index c83807a..d707f8d 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -47,9 +47,9 @@ echo
 echo "============================================================================="
 echo
 
-echo "Running the test program with 10 days postive offset specified, and sped up 2 times"
-echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"+10d x2\" ./timetest"
-LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="+10d x2" NO_FAKE_STAT=1 ./timetest
+echo "Running the test program with 10 days positive offset specified, and speed-up factor"
+echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"+10d x1\" ./timetest"
+LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="+10d x1" NO_FAKE_STAT=1 ./timetest
 echo
 
 echo "============================================================================="
diff --git a/test/test_OSX.sh b/test/test_OSX.sh
index b6e5177..afff4cb 100755
--- a/test/test_OSX.sh
+++ b/test/test_OSX.sh
@@ -33,14 +33,19 @@ echo "FAKETIME=\"-10d\" NO_FAKE_STAT=1 ./timetest"
 FAKETIME="-10d" NO_FAKE_STAT=1 ./timetest
 echo
 
-echo "Running the test program with 10 days postive offset specified, and sped up 2 times"
+echo "Running the test program with 10 days positive offset specified, and sped up 2 times"
 echo "FAKETIME=\"+10d x2\" ./timetest"
 FAKETIME="+10d x2" NO_FAKE_STAT=1 ./timetest
 echo
 
-echo "Running the 'date' command with 15 days negative offset specified"
-echo "FAKETIME=\"-15d\" date"
-FAKETIME="-15d" date
-echo
+# On more recent macOS versions, the following won't work without copying gdate
+# to a different folder beforehand. We don't do that here, and since the output
+# of these tests must be reviewed manually anyway, the timetest binary should be
+# sufficient to determine whether everything works as planned.
+
+#echo "Running the 'date' command with 15 days negative offset specified"
+#echo "FAKETIME=\"-15d\" date"
+#FAKETIME="-15d" date
+#echo
 
 exit 0
diff --git a/test/timetest.c b/test/timetest.c
index 79597b0..4e3515c 100644
--- a/test/timetest.c
+++ b/test/timetest.c
@@ -18,6 +18,7 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
@@ -31,6 +32,8 @@
 #endif
 
 #ifndef __APPLE__
+#include <pthread.h>
+#include <errno.h>
 #include <signal.h>
 
 #define VERBOSE 0
@@ -49,6 +52,64 @@ handler(int sig, siginfo_t *si, void *uc)
     printf("Caught signal %d\n", sig);
   }
 }
+
+void* pthread_test(void* args)
+{
+  pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
+  pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
+
+  pthread_cond_t monotonic_cond;
+  pthread_condattr_t attr;
+
+  struct timespec timeToWait, now;
+  int rt;
+
+  args = args; // silence compiler warning about unused argument
+
+  clock_gettime(CLOCK_REALTIME, &now);
+  timeToWait.tv_sec = now.tv_sec+1;
+  timeToWait.tv_nsec = now.tv_nsec;
+
+  printf("pthread_cond_timedwait: CLOCK_REALTIME test\n");
+  printf("(Intentionally sleeping 1 second...)\n");
+  fflush(stdout);
+
+  pthread_mutex_lock(&fakeMutex);
+  rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
+  if (rt != ETIMEDOUT)
+  {
+    printf("pthread_cond_timedwait failed\n");
+    exit(EXIT_FAILURE);
+  }
+  pthread_mutex_unlock(&fakeMutex);
+
+
+  pthread_condattr_init(&attr);
+  pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+  pthread_cond_init(&monotonic_cond, &attr);
+
+  clock_gettime(CLOCK_MONOTONIC, &now);
+  timeToWait.tv_sec = now.tv_sec+1;
+  timeToWait.tv_nsec = now.tv_nsec;
+
+  printf("pthread_cond_timedwait: CLOCK_MONOTONIC test\n");
+  printf("(Intentionally sleeping 1 second...)\n");
+  fflush(stdout);
+
+  pthread_mutex_lock(&fakeMutex);
+  rt = pthread_cond_timedwait(&monotonic_cond, &fakeMutex, &timeToWait);
+  if (rt != ETIMEDOUT)
+  {
+    printf("pthread_cond_timedwait failed\n");
+    exit(EXIT_FAILURE);
+  }
+  pthread_mutex_unlock(&fakeMutex);
+
+  pthread_cond_destroy(&monotonic_cond);
+
+  return NULL;
+}
+
 #endif
 
 int main (int argc, char **argv)
@@ -69,6 +130,12 @@ int main (int argc, char **argv)
 #endif
 
 #ifndef __APPLE__
+    pthread_t thread;
+    void *ret;
+
+    pthread_create(&thread, NULL, pthread_test, NULL);
+    pthread_join(thread, &ret);
+
     sa.sa_flags = SA_SIGINFO;
     sa.sa_sigaction = handler;
     sigemptyset(&sa.sa_mask);
openSUSE Build Service is sponsored by