From 1fa53c5bf8d0717f784c79abaa5111f88ab00221 Mon Sep 17 00:00:00 2001
From: Toni Uhlig <matzeton@googlemail.com>
Date: Wed, 15 Sep 2021 17:04:21 +0200
Subject: Squashed 'dependencies/uthash/' changes from 8e67ced..bf15263

bf15263 Fix a "bug" in the example where option 3 interfered with option 1's counter.
b6e24ef Use `malloc(sizeof *s)` in example code.
a109c6b Stop using `gets` in example.c.
c85c9e1 fix: fix utstack example's compiling error
86e6776 Replace *.github.com urls with *.github.io (#227)
e493aa9 Bump version to 2.3.0.
ae2ac52 Fix README.md to display the *actual* TravisCI status.
134e241 Silence -Wswitch-default warnings, and add it to the TravisCI config.
62fefa6 Fix some typos in userguide.txt, and re-remove spaces in macro definitions.
37d2021 tests: add whitespaces to example code
524ca1a doc: add whitespaces to documentation
0f6c619 Fix a typo in the documentation for HASH_COUNT. NFC.
388134a Rename uthash_memcmp to HASH_KEYCMP, step 3.
053bed1 Eliminate HASH_FCN; change the handling of HASH_FUNCTION to match HASH_KEYCMP.
f0e1bd9 Refactor test93.c to avoid scan-build warnings.
45af88c Remove two dead writes in tests, to silence scan-build warnings.
66e2668 Bump version to 2.2.0.
973bd67 uthash.h: Swap multiplicands to put the widest ones first.
15ad042 Always include <stdint.h>, unless HASH_NO_STDINT is defined by the user.
6b4768b Rename uthash_memcmp to HASH_KEYCMP, step 2.
e64c7f0 Update tests/README to describe the most recently added tests. NFC.
c62796c HASH_CLEAR after some tests, to eliminate "memory leak" warnings.
7f0aadb Support spaces in $exe path
0831d9a uthash.h: fix compiler warning -Wcast-qual
ba2fbfd utarray.h: preserve constness in utarray_str_cpy

git-subtree-dir: dependencies/uthash
git-subtree-split: bf15263081be6229be31addd48566df93921cb46
---
 .travis.yml          |   2 +-
 LICENSE              |   2 +-
 README.md            |   4 +-
 doc/ChangeLog.txt    |  17 +++-
 doc/index.html       |   8 +-
 doc/license.html     |   4 +-
 doc/userguide.txt    | 222 +++++++++++++++++++++++++++------------------------
 doc/utarray.txt      |   2 +-
 doc/utlist.txt       |   2 +-
 doc/utringbuffer.txt |   2 +-
 doc/utstack.txt      |   2 +-
 doc/utstring.txt     |   2 +-
 package.json         |   2 +-
 src/utarray.h        |   9 ++-
 src/uthash.h         |  64 ++++++---------
 src/utlist.h         |   4 +-
 src/utringbuffer.h   |   4 +-
 src/utstack.h        |   8 +-
 src/utstring.h       |   4 +-
 tests/Makefile       |   2 +-
 tests/README         |   9 ++-
 tests/example.c      |  86 ++++++++++----------
 tests/hashscan.c     |   2 +-
 tests/keystats       |   4 +-
 tests/test10.c       |   3 +
 tests/test6.c        |  13 +--
 tests/test65.c       |   2 +-
 tests/test76.c       |   7 +-
 tests/test77.c       |   4 +-
 tests/test88.ans     |  20 ++---
 tests/test88.c       |   8 +-
 tests/test93.c       |  37 ++++-----
 tests/test96.ans     |  40 ++++++++++
 tests/test96.c       |  48 +++++++++++
 34 files changed, 377 insertions(+), 272 deletions(-)
 create mode 100644 tests/test96.ans
 create mode 100644 tests/test96.c

diff --git a/.travis.yml b/.travis.yml
index 4e51ee2d9..62646943b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,7 +7,7 @@ matrix:
     compiler: clang
   - os: osx
 script:
-- make -C tests EXTRA_CFLAGS="-W -Wall -Wextra"
+- make -C tests EXTRA_CFLAGS="-W -Wall -Wextra -Wswitch-default"
 - make -C tests clean ; make -C tests pedantic
 - make -C tests clean ; make -C tests pedantic EXTRA_CFLAGS=-DNO_DECLTYPE
 - make -C tests clean ; make -C tests cplusplus
diff --git a/LICENSE b/LICENSE
index af78128c9..75107151b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2018, Troy D. Hanson    http://troydhanson.github.com/uthash/
+Copyright (c) 2005-2021, Troy D. Hanson    http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
index 776f368fd..e240660b7 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
 
-[![Build status](https://travis-ci.org/Quuxplusone/uthash.svg?branch=travis-ci)](https://travis-ci.org/troydhanson/uthash)
+[![Build status](https://api.travis-ci.org/troydhanson/uthash.svg?branch=master)](https://travis-ci.org/troydhanson/uthash)
 
 Documentation for uthash is available at:
 
-http://troydhanson.github.com/uthash/
+https://troydhanson.github.io/uthash/
 
 
diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt
index 281de80fe..3fa955782 100644
--- a/doc/ChangeLog.txt
+++ b/doc/ChangeLog.txt
@@ -5,6 +5,21 @@ Click to return to the link:index.html[uthash home page].
 
 NOTE: This ChangeLog may be incomplete and/or incorrect. See the git commit log.
 
+Version 2.3.0 (2021-02-25)
+--------------------------
+* remove HASH_FCN; the HASH_FUNCTION and HASH_KEYCMP macros now behave similarly
+* remove uthash_memcmp (deprecated in v2.1.0) in favor of HASH_KEYCMP
+* silence -Wswitch-default warnings (thanks, Olaf Bergmann!)
+
+Version 2.2.0 (2020-12-17)
+--------------------------
+* add HASH_NO_STDINT for platforms without C99 <stdint.h>
+* silence many -Wcast-qual warnings (thanks, Olaf Bergmann!)
+* skip hash computation when finding in an empty hash (thanks, Huansong Fu!)
+* rename oom to utarray_oom, in utarray.h (thanks, Hong Xu!)
+* rename oom to utstring_oom, in utstring.h (thanks, Hong Xu!)
+* remove MurmurHash/HASH_MUR
+
 Version 2.1.0 (2018-12-20)
 --------------------------
 * silence some Clang static analysis warnings
@@ -56,7 +71,7 @@ Version 1.9.8 (2013-03-10)
 * `HASH_REPLACE` now in uthash (thanks, Nick Vatamaniuc!)
 * fixed clang warnings (thanks wynnw!)
 * fixed `utarray_insert` when inserting past array end (thanks Rob Willett!)
-* you can now find http://troydhanson.github.com/uthash/[uthash on GitHub]
+* you can now find http://troydhanson.github.io/uthash/[uthash on GitHub]
 * there's a https://groups.google.com/d/forum/uthash[uthash Google Group]
 * uthash has been downloaded 29,000+ times since 2006 on SourceForge
 
diff --git a/doc/index.html b/doc/index.html
index 11b120e22..2f86ba1dc 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -14,7 +14,7 @@
 
   <div id="topnav">
   <a href="http://github.com/troydhanson/uthash">GitHub page</a> &gt;
-  uthash home <!-- http://troydhanson.github.com/uthash/ -->
+  uthash home <!-- http://troydhanson.github.io/uthash/ -->
 
 <a href="https://twitter.com/share" class="twitter-share-button" data-via="troydhanson">Tweet</a>
 <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
@@ -72,7 +72,7 @@ struct my_struct {
 struct my_struct *users = NULL;
 
 void add_user(struct my_struct *s) {
-    HASH_ADD_INT( users, id, s );
+    HASH_ADD_INT(users, id, s);
 }
 
 </pre>
@@ -86,7 +86,7 @@ Example 2. Looking up an item in a hash.
 struct my_struct *find_user(int user_id) {
     struct my_struct *s;
 
-    HASH_FIND_INT( users, &amp;user_id, s );
+    HASH_FIND_INT(users, &amp;user_id, s);
     return s;
 }
 
@@ -100,7 +100,7 @@ Example 3. Deleting an item from a hash.
 
 <pre>
 void delete_user(struct my_struct *user) {
-    HASH_DEL( users, user);
+    HASH_DEL(users, user);
 }
 
 </pre>
diff --git a/doc/license.html b/doc/license.html
index c70684558..9a4b9ef35 100644
--- a/doc/license.html
+++ b/doc/license.html
@@ -13,7 +13,7 @@
   </div> <!-- banner -->
 
   <div id="topnav">
-  <a href="http://troydhanson.github.com/uthash/">uthash home</a> &gt;
+  <a href="http://troydhanson.github.io/uthash/">uthash home</a> &gt;
   BSD license
   </div>
 
@@ -21,7 +21,7 @@
   <div id="mid">
       <div id="main">
 <pre>
-Copyright (c) 2005-2018, Troy D. Hanson  http://troydhanson.github.com/uthash/
+Copyright (c) 2005-2021, Troy D. Hanson  http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/doc/userguide.txt b/doc/userguide.txt
index 4d8ce8f0a..39fa7d1ed 100644
--- a/doc/userguide.txt
+++ b/doc/userguide.txt
@@ -1,7 +1,7 @@
 uthash User Guide
 =================
 Troy D. Hanson, Arthur O'Dwyer
-v2.1.0, December 2018
+v2.3.0, February 2021
 
 To download uthash, follow this link back to the
 https://github.com/troydhanson/uthash[GitHub project page].
@@ -215,10 +215,10 @@ a unique value. Then call `HASH_ADD`. (Here we use the convenience macro
 void add_user(int user_id, char *name) {
     struct my_struct *s;
 
-    s = malloc(sizeof(struct my_struct));
+    s = malloc(sizeof *s);
     s->id = user_id;
     strcpy(s->name, name);
-    HASH_ADD_INT( users, id, s );  /* id: name of key field */
+    HASH_ADD_INT(users, id, s);  /* id: name of key field */
 }
 ----------------------------------------------------------------------
 
@@ -227,7 +227,7 @@ second parameter is the 'name' of the key field. Here, this is `id`. The
 last parameter is a pointer to the structure being added.
 
 [[validc]]
-.Wait.. the field name is a parameter?
+.Wait.. the parameter is a field name?
 *******************************************************************************
 If you find it strange that `id`, which is the 'name of a field' in the
 structure, can be passed as a parameter... welcome to the world of macros. Don't
@@ -256,10 +256,10 @@ Otherwise we just modify the structure that already exists.
       struct my_struct *s;
 
       HASH_FIND_INT(users, &user_id, s);  /* id already in the hash? */
-      if (s==NULL) {
+      if (s == NULL) {
         s = (struct my_struct *)malloc(sizeof *s);
         s->id = user_id;
-        HASH_ADD_INT( users, id, s );  /* id: name of key field */
+        HASH_ADD_INT(users, id, s);  /* id: name of key field */
       }
       strcpy(s->name, name);
   }
@@ -284,7 +284,7 @@ right.
   /* bad */
   void add_user(struct my_struct *users, int user_id, char *name) {
     ...
-    HASH_ADD_INT( users, id, s );
+    HASH_ADD_INT(users, id, s);
   }
 
 You really need to pass 'a pointer' to the hash pointer:
@@ -292,7 +292,7 @@ You really need to pass 'a pointer' to the hash pointer:
   /* good */
   void add_user(struct my_struct **users, int user_id, char *name) { ...
     ...
-    HASH_ADD_INT( *users, id, s );
+    HASH_ADD_INT(*users, id, s);
   }
 
 Note that we dereferenced the pointer in the `HASH_ADD` also.
@@ -319,7 +319,7 @@ To look up a structure in a hash, you need its key.  Then call `HASH_FIND`.
 struct my_struct *find_user(int user_id) {
     struct my_struct *s;
 
-    HASH_FIND_INT( users, &user_id, s );  /* s: output pointer */
+    HASH_FIND_INT(users, &user_id, s);  /* s: output pointer */
     return s;
 }
 ----------------------------------------------------------------------
@@ -376,8 +376,8 @@ void delete_all() {
   struct my_struct *current_user, *tmp;
 
   HASH_ITER(hh, users, current_user, tmp) {
-    HASH_DEL(users,current_user);  /* delete; users advances to next */
-    free(current_user);            /* optional- if you want to free  */
+    HASH_DEL(users, current_user);  /* delete; users advances to next */
+    free(current_user);             /* optional- if you want to free  */
   }
 }
 ----------------------------------------------------------------------
@@ -387,7 +387,7 @@ All-at-once deletion
 If you only want to delete all the items, but not free them or do any
 per-element clean up, you can do this more efficiently in a single operation:
 
-  HASH_CLEAR(hh,users);
+  HASH_CLEAR(hh, users);
 
 Afterward, the list head (here, `users`) will be set to `NULL`.
 
@@ -403,7 +403,7 @@ num_users = HASH_COUNT(users);
 printf("there are %u users\n", num_users);
 ----------------------------------------------------------------------
 
-Incidentally, this works even the list (`users`, here) is `NULL`, in
+Incidentally, this works even if the list head (here, `users`) is `NULL`, in
 which case the count is 0.
 
 Iterating and sorting
@@ -417,7 +417,7 @@ following the `hh.next` pointer.
 void print_users() {
     struct my_struct *s;
 
-    for(s=users; s != NULL; s=s->hh.next) {
+    for (s = users; s != NULL; s = s->hh.next) {
         printf("user id %d: name %s\n", s->id, s->name);
     }
 }
@@ -430,7 +430,7 @@ the hash, starting from any known item.
 Deletion-safe iteration
 ^^^^^^^^^^^^^^^^^^^^^^^
 In the example above, it would not be safe to delete and free `s` in the body
-of the 'for' loop, (because `s` is derefenced each time the loop iterates).
+of the 'for' loop, (because `s` is dereferenced each time the loop iterates).
 This is easy to rewrite correctly (by copying the `s->hh.next` pointer to a
 temporary variable 'before' freeing `s`), but it comes up often enough that a
 deletion-safe iteration macro, `HASH_ITER`, is included. It expands to a
@@ -452,14 +452,14 @@ doubly-linked list.
 *******************************************************************************
 
 If you're using uthash in a C++ program, you need an extra cast on the `for`
-iterator, e.g., `s=(struct my_struct*)s->hh.next`.
+iterator, e.g., `s = static_cast<my_struct*>(s->hh.next)`.
 
 Sorting
 ^^^^^^^
 The items in the hash are visited in "insertion order" when you follow the
 `hh.next` pointer. You can sort the items into a new order using `HASH_SORT`.
 
-    HASH_SORT( users, name_sort );
+    HASH_SORT(users, name_sort);
 
 The second argument is a pointer to a comparison function. It must accept two
 pointer arguments (the items to compare), and must return an `int` which is
@@ -479,20 +479,20 @@ Below, `name_sort` and `id_sort` are two examples of sort functions.
 
 .Sorting the items in the hash
 ----------------------------------------------------------------------
-int name_sort(struct my_struct *a, struct my_struct *b) {
-    return strcmp(a->name,b->name);
+int by_name(const struct my_struct *a, const struct my_struct *b) {
+    return strcmp(a->name, b->name);
 }
 
-int id_sort(struct my_struct *a, struct my_struct *b) {
+int by_id(const struct my_struct *a, const struct my_struct *b) {
     return (a->id - b->id);
 }
 
 void sort_by_name() {
-    HASH_SORT(users, name_sort);
+    HASH_SORT(users, by_name);
 }
 
 void sort_by_id() {
-    HASH_SORT(users, id_sort);
+    HASH_SORT(users, by_id);
 }
 ----------------------------------------------------------------------
 
@@ -516,85 +516,100 @@ Follow the prompts to try the program.
 
 .A complete program
 ----------------------------------------------------------------------
-#include <stdio.h>   /* gets */
+#include <stdio.h>   /* printf */
 #include <stdlib.h>  /* atoi, malloc */
 #include <string.h>  /* strcpy */
 #include "uthash.h"
 
 struct my_struct {
     int id;                    /* key */
-    char name[10];
+    char name[21];
     UT_hash_handle hh;         /* makes this structure hashable */
 };
 
 struct my_struct *users = NULL;
 
-void add_user(int user_id, char *name) {
+void add_user(int user_id, const char *name)
+{
     struct my_struct *s;
 
     HASH_FIND_INT(users, &user_id, s);  /* id already in the hash? */
-    if (s==NULL) {
-      s = (struct my_struct *)malloc(sizeof *s);
-      s->id = user_id;
-      HASH_ADD_INT( users, id, s );  /* id: name of key field */
+    if (s == NULL) {
+        s = (struct my_struct*)malloc(sizeof *s);
+        s->id = user_id;
+        HASH_ADD_INT(users, id, s);  /* id is the key field */
     }
     strcpy(s->name, name);
 }
 
-struct my_struct *find_user(int user_id) {
+struct my_struct *find_user(int user_id)
+{
     struct my_struct *s;
 
-    HASH_FIND_INT( users, &user_id, s );  /* s: output pointer */
+    HASH_FIND_INT(users, &user_id, s);  /* s: output pointer */
     return s;
 }
 
-void delete_user(struct my_struct *user) {
+void delete_user(struct my_struct *user)
+{
     HASH_DEL(users, user);  /* user: pointer to deletee */
     free(user);
 }
 
-void delete_all() {
-  struct my_struct *current_user, *tmp;
+void delete_all()
+{
+    struct my_struct *current_user;
+    struct my_struct *tmp;
 
-  HASH_ITER(hh, users, current_user, tmp) {
-    HASH_DEL(users, current_user);  /* delete it (users advances to next) */
-    free(current_user);             /* free it */
-  }
+    HASH_ITER(hh, users, current_user, tmp) {
+        HASH_DEL(users, current_user);  /* delete it (users advances to next) */
+        free(current_user);             /* free it */
+    }
 }
 
-void print_users() {
+void print_users()
+{
     struct my_struct *s;
 
-    for(s=users; s != NULL; s=(struct my_struct*)(s->hh.next)) {
+    for (s = users; s != NULL; s = (struct my_struct*)(s->hh.next)) {
         printf("user id %d: name %s\n", s->id, s->name);
     }
 }
 
-int name_sort(struct my_struct *a, struct my_struct *b) {
-    return strcmp(a->name,b->name);
+int by_name(const struct my_struct *a, const struct my_struct *b)
+{
+    return strcmp(a->name, b->name);
 }
 
-int id_sort(struct my_struct *a, struct my_struct *b) {
+int by_id(const struct my_struct *a, const struct my_struct *b)
+{
     return (a->id - b->id);
 }
 
-void sort_by_name() {
-    HASH_SORT(users, name_sort);
-}
-
-void sort_by_id() {
-    HASH_SORT(users, id_sort);
+const char *getl(const char *prompt)
+{
+    static char buf[21];
+    char *p;
+    printf("%s? ", prompt); fflush(stdout);
+    p = fgets(buf, sizeof(buf), stdin);
+    if (p == NULL || (p = strchr(buf, '\n')) == NULL) {
+        puts("Invalid input!");
+        exit(EXIT_FAILURE);
+    }
+    *p = '\0';
+    return buf;
 }
 
-int main(int argc, char *argv[]) {
-    char in[10];
-    int id=1, running=1;
+int main()
+{
+    int id = 1;
+    int running = 1;
     struct my_struct *s;
-    unsigned num_users;
+    int temp;
 
     while (running) {
         printf(" 1. add user\n");
-        printf(" 2. add/rename user by id\n");
+        printf(" 2. add or rename user by id\n");
         printf(" 3. find user\n");
         printf(" 4. delete user\n");
         printf(" 5. delete all users\n");
@@ -603,47 +618,44 @@ int main(int argc, char *argv[]) {
         printf(" 8. print users\n");
         printf(" 9. count users\n");
         printf("10. quit\n");
-        gets(in);
-        switch(atoi(in)) {
+        switch (atoi(getl("Command"))) {
             case 1:
-                printf("name?\n");
-                add_user(id++, gets(in));
+                add_user(id++, getl("Name (20 char max)"));
                 break;
             case 2:
-                printf("id?\n");
-                gets(in); id = atoi(in);
-                printf("name?\n");
-                add_user(id, gets(in));
+                temp = atoi(getl("ID"));
+                add_user(temp, getl("Name (20 char max)"));
                 break;
             case 3:
-                printf("id?\n");
-                s = find_user(atoi(gets(in)));
+                s = find_user(atoi(getl("ID to find")));
                 printf("user: %s\n", s ? s->name : "unknown");
                 break;
             case 4:
-                printf("id?\n");
-                s = find_user(atoi(gets(in)));
-                if (s) delete_user(s);
-                else printf("id unknown\n");
+                s = find_user(atoi(getl("ID to delete")));
+                if (s) {
+                    delete_user(s);
+                } else {
+                    printf("id unknown\n");
+                }
                 break;
             case 5:
                 delete_all();
                 break;
             case 6:
-                sort_by_name();
+                HASH_SORT(users, by_name);
                 break;
             case 7:
-                sort_by_id();
+                HASH_SORT(users, by_id);
                 break;
             case 8:
                 print_users();
                 break;
             case 9:
-                num_users=HASH_COUNT(users);
-                printf("there are %u users\n", num_users);
+                temp = HASH_COUNT(users);
+                printf("there are %d users\n", temp);
                 break;
             case 10:
-                running=0;
+                running = 0;
                 break;
         }
     }
@@ -720,10 +732,10 @@ int main(int argc, char *argv[]) {
         s = (struct my_struct *)malloc(sizeof *s);
         strcpy(s->name, names[i]);
         s->id = i;
-        HASH_ADD_STR( users, name, s );
+        HASH_ADD_STR(users, name, s);
     }
 
-    HASH_FIND_STR( users, "betty", s);
+    HASH_FIND_STR(users, "betty", s);
     if (s) printf("betty's id is %d\n", s->id);
 
     /* free the hash table contents */
@@ -766,10 +778,10 @@ int main(int argc, char *argv[]) {
         s = (struct my_struct *)malloc(sizeof *s);
         s->name = names[i];
         s->id = i;
-        HASH_ADD_KEYPTR( hh, users, s->name, strlen(s->name), s );
+        HASH_ADD_KEYPTR(hh, users, s->name, strlen(s->name), s);
     }
 
-    HASH_FIND_STR( users, "betty", s);
+    HASH_FIND_STR(users, "betty", s);
     if (s) printf("betty's id is %d\n", s->id);
 
     /* free the hash table contents */
@@ -812,12 +824,12 @@ int main() {
   if (!e) return -1;
   e->key = (void*)someaddr;
   e->i = 1;
-  HASH_ADD_PTR(hash,key,e);
+  HASH_ADD_PTR(hash, key, e);
   HASH_FIND_PTR(hash, &someaddr, d);
   if (d) printf("found\n");
 
   /* release memory */
-  HASH_DEL(hash,e);
+  HASH_DEL(hash, e);
   free(e);
   return 0;
 }
@@ -924,7 +936,7 @@ int main(int argc, char *argv[]) {
     int beijing[] = {0x5317, 0x4eac};   /* UTF-32LE for 北京 */
 
     /* allocate and initialize our structure */
-    msg = (msg_t *)malloc( sizeof(msg_t) + sizeof(beijing) );
+    msg = (msg_t *)malloc(sizeof(msg_t) + sizeof(beijing));
     memset(msg, 0, sizeof(msg_t)+sizeof(beijing)); /* zero fill */
     msg->len = sizeof(beijing);
     msg->encoding = UTF32;
@@ -936,16 +948,16 @@ int main(int argc, char *argv[]) {
              - offsetof(msg_t, encoding);  /* offset of first key field */
 
     /* add our structure to the hash table */
-    HASH_ADD( hh, msgs, encoding, keylen, msg);
+    HASH_ADD(hh, msgs, encoding, keylen, msg);
 
     /* look it up to prove that it worked :-) */
-    msg=NULL;
+    msg = NULL;
 
     lookup_key = (lookup_key_t *)malloc(sizeof(*lookup_key) + sizeof(beijing));
     memset(lookup_key, 0, sizeof(*lookup_key) + sizeof(beijing));
     lookup_key->encoding = UTF32;
     memcpy(lookup_key->text, beijing, sizeof(beijing));
-    HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg );
+    HASH_FIND(hh, msgs, &lookup_key->encoding, keylen, msg);
     if (msg) printf("found \n");
     free(lookup_key);
 
@@ -1028,7 +1040,7 @@ typedef struct item {
   UT_hash_handle hh;
 } item_t;
 
-item_t *items=NULL;
+item_t *items = NULL;
 
 int main(int argc, char *argvp[]) {
   item_t *item1, *item2, *tmp1, *tmp2;
@@ -1119,7 +1131,7 @@ always used with the `users_by_name` hash table).
     int i;
     char *name;
 
-    s = malloc(sizeof(struct my_struct));
+    s = malloc(sizeof *s);
     s->id = 1;
     strcpy(s->username, "thanson");
 
@@ -1128,7 +1140,7 @@ always used with the `users_by_name` hash table).
     HASH_ADD(hh2, users_by_name, username, strlen(s->username), s);
 
     /* find user by ID in the "users_by_id" hash table */
-    i=1;
+    i = 1;
     HASH_FIND(hh1, users_by_id, &i, sizeof(int), s);
     if (s) printf("found id %d: %s\n", i, s->username);
 
@@ -1155,7 +1167,7 @@ The `HASH_ADD_INORDER*` macros work just like their `HASH_ADD*` counterparts, bu
 with an additional comparison-function argument:
 
   int name_sort(struct my_struct *a, struct my_struct *b) {
-    return strcmp(a->name,b->name);
+    return strcmp(a->name, b->name);
   }
 
   HASH_ADD_KEYPTR_INORDER(hh, items, &item->name, strlen(item->name), item, name_sort);
@@ -1183,7 +1195,7 @@ Now we can define two sort functions, then use `HASH_SRT`.
   }
 
   int sort_by_name(struct my_struct *a, struct my_struct *b) {
-    return strcmp(a->username,b->username);
+    return strcmp(a->username, b->username);
   }
 
   HASH_SRT(hh1, users_by_id, sort_by_id);
@@ -1240,7 +1252,8 @@ for a structure to be usable with `HASH_SELECT`, it must have two or more hash
 handles. (As described <<multihash,here>>, a structure can exist in many
 hash tables at the same time; it must have a separate hash handle for each one).
 
-  user_t *users=NULL, *admins=NULL; /* two hash tables */
+  user_t *users = NULL;   /* hash table of users */
+  user_t *admins = NULL;  /* hash table of admins */
 
   typedef struct {
       int id;
@@ -1252,25 +1265,26 @@ Now suppose we have added some users, and want to select just the administrator
 users who have id's less than 1024.
 
   #define is_admin(x) (((user_t*)x)->id < 1024)
-  HASH_SELECT(ah,admins,hh,users,is_admin);
+  HASH_SELECT(ah, admins, hh, users, is_admin);
 
 The first two parameters are the 'destination' hash handle and hash table, the
 second two parameters are the 'source' hash handle and hash table, and the last
-parameter is the 'select condition'. Here we used a macro `is_admin()` but we
+parameter is the 'select condition'. Here we used a macro `is_admin(x)` but we
 could just as well have used a function.
 
-  int is_admin(void *userv) {
-    user_t *user = (user_t*)userv;
+  int is_admin(const void *userv) {
+    user_t *user = (const user_t*)userv;
     return (user->id < 1024) ? 1 : 0;
   }
 
 If the select condition always evaluates to true, this operation is
-essentially a 'merge' of the source hash into the destination hash. Of course,
-the source hash remains unchanged under any use of `HASH_SELECT`. It only adds
-items to the destination hash selectively.
+essentially a 'merge' of the source hash into the destination hash.
+
+`HASH_SELECT` adds items to the destination without removing them from
+the source; the source hash table remains unchanged. The destination hash table
+must not be the same as the source hash table.
 
-The two hash handles must differ. An example of using `HASH_SELECT` is included
-in `tests/test36.c`.
+An example of using `HASH_SELECT` is included in `tests/test36.c`.
 
 [[hash_keycompare]]
 Specifying an alternate key comparison function
@@ -1290,7 +1304,7 @@ that do not provide `memcmp`, you can substitute your own implementation.
 
 ----------------------------------------------------------------------------
 #undef HASH_KEYCMP
-#define HASH_KEYCMP(a,b,len) bcmp(a,b,len)
+#define HASH_KEYCMP(a,b,len) bcmp(a, b, len)
 ----------------------------------------------------------------------------
 
 Another reason to substitute your own key comparison function is if your "key" is not
@@ -1631,7 +1645,7 @@ If your application uses its own custom allocator, uthash can use them too.
 
 /* re-define, specifying alternate functions */
 #define uthash_malloc(sz) my_malloc(sz)
-#define uthash_free(ptr,sz) my_free(ptr)
+#define uthash_free(ptr, sz) my_free(ptr)
 
 ...
 ----------------------------------------------------------------------------
@@ -1647,7 +1661,7 @@ provide these functions, you can substitute your own implementations.
 
 ----------------------------------------------------------------------------
 #undef uthash_bzero
-#define uthash_bzero(a,len) my_bzero(a,len)
+#define uthash_bzero(a, len) my_bzero(a, len)
 
 #undef uthash_strlen
 #define uthash_strlen(s) my_strlen(s)
@@ -1754,7 +1768,7 @@ concurrent readers (since uthash 1.5).
 For example using pthreads you can create an rwlock like this:
 
   pthread_rwlock_t lock;
-  if (pthread_rwlock_init(&lock,NULL) != 0) fatal("can't create rwlock");
+  if (pthread_rwlock_init(&lock, NULL) != 0) fatal("can't create rwlock");
 
 Then, readers must acquire the read lock before doing any `HASH_FIND` calls or
 before iterating over the hash elements:
@@ -1795,10 +1809,10 @@ In order to use the convenience macros,
 |===============================================================================
 |macro            | arguments
 |HASH_ADD_INT     | (head, keyfield_name, item_ptr)
-|HASH_REPLACE_INT | (head, keyfiled_name, item_ptr,replaced_item_ptr)
+|HASH_REPLACE_INT | (head, keyfield_name, item_ptr, replaced_item_ptr)
 |HASH_FIND_INT    | (head, key_ptr, item_ptr)
 |HASH_ADD_STR     | (head, keyfield_name, item_ptr)
-|HASH_REPLACE_STR | (head,keyfield_name, item_ptr, replaced_item_ptr)
+|HASH_REPLACE_STR | (head, keyfield_name, item_ptr, replaced_item_ptr)
 |HASH_FIND_STR    | (head, key_ptr, item_ptr)
 |HASH_ADD_PTR     | (head, keyfield_name, item_ptr)
 |HASH_REPLACE_PTR | (head, keyfield_name, item_ptr, replaced_item_ptr)
diff --git a/doc/utarray.txt b/doc/utarray.txt
index 25d94e260..8ef940b53 100644
--- a/doc/utarray.txt
+++ b/doc/utarray.txt
@@ -1,7 +1,7 @@
 utarray: dynamic array macros for C
 ===================================
 Troy D. Hanson <tdh@tkhanson.net>
-v2.1.0, December 2018
+v2.3.0, February 2021
 
 Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
 
diff --git a/doc/utlist.txt b/doc/utlist.txt
index 06b6b00dc..47dc625ef 100644
--- a/doc/utlist.txt
+++ b/doc/utlist.txt
@@ -1,7 +1,7 @@
 utlist: linked list macros for C structures
 ===========================================
 Troy D. Hanson <tdh@tkhanson.net>
-v2.1.0, December 2018
+v2.3.0, February 2021
 
 Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
 
diff --git a/doc/utringbuffer.txt b/doc/utringbuffer.txt
index 86206c1e5..02eb6cf5f 100644
--- a/doc/utringbuffer.txt
+++ b/doc/utringbuffer.txt
@@ -1,7 +1,7 @@
 utringbuffer: dynamic ring-buffer macros for C
 ==============================================
 Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
-v2.1.0, December 2018
+v2.3.0, February 2021
 
 Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
 
diff --git a/doc/utstack.txt b/doc/utstack.txt
index 0c628c06d..bcfc3bffb 100644
--- a/doc/utstack.txt
+++ b/doc/utstack.txt
@@ -1,7 +1,7 @@
 utstack: intrusive stack macros for C
 =====================================
 Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
-v2.1.0, December 2018
+v2.3.0, February 2021
 
 Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
 
diff --git a/doc/utstring.txt b/doc/utstring.txt
index 56e2f4c00..30678591c 100644
--- a/doc/utstring.txt
+++ b/doc/utstring.txt
@@ -1,7 +1,7 @@
 utstring: dynamic string macros for C
 =====================================
 Troy D. Hanson <tdh@tkhanson.net>
-v2.1.0, December 2018
+v2.3.0, February 2021
 
 Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
 
diff --git a/package.json b/package.json
index 28377541f..b5a2f241e 100644
--- a/package.json
+++ b/package.json
@@ -24,5 +24,5 @@
     "src/utstring.h"
   ],
 
-  "version": "2.1.0"
+  "version": "2.3.0"
 }
diff --git a/src/utarray.h b/src/utarray.h
index 6b6201820..3a5106666 100644
--- a/src/utarray.h
+++ b/src/utarray.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2018, Troy D. Hanson   http://troydhanson.github.com/uthash/
+Copyright (c) 2008-2021, Troy D. Hanson   http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef UTARRAY_H
 #define UTARRAY_H
 
-#define UTARRAY_VERSION 2.1.0
+#define UTARRAY_VERSION 2.3.0
 
 #include <stddef.h>  /* size_t */
 #include <string.h>  /* memset, etc */
@@ -232,8 +232,9 @@ typedef struct {
 
 /* last we pre-define a few icd for common utarrays of ints and strings */
 static void utarray_str_cpy(void *dst, const void *src) {
-  char **_src = (char**)src, **_dst = (char**)dst;
-  *_dst = (*_src == NULL) ? NULL : strdup(*_src);
+  char *const *srcc = (char *const *)src;
+  char **dstc = (char**)dst;
+  *dstc = (*srcc == NULL) ? NULL : strdup(*srcc);
 }
 static void utarray_str_dtor(void *elt) {
   char **eltc = (char**)elt;
diff --git a/src/uthash.h b/src/uthash.h
index 5e5866a35..9a396b617 100644
--- a/src/uthash.h
+++ b/src/uthash.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2003-2018, Troy D. Hanson     http://troydhanson.github.com/uthash/
+Copyright (c) 2003-2021, Troy D. Hanson     http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -24,12 +24,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef UTHASH_H
 #define UTHASH_H
 
-#define UTHASH_VERSION 2.1.0
+#define UTHASH_VERSION 2.3.0
 
 #include <string.h>   /* memcmp, memset, strlen */
 #include <stddef.h>   /* ptrdiff_t */
 #include <stdlib.h>   /* exit */
 
+#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT
+/* This codepath is provided for backward compatibility, but I plan to remove it. */
+#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead"
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT
+#else
+#include <stdint.h>   /* uint8_t, uint32_t */
+#endif
+
 /* 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
@@ -62,23 +72,6 @@ do {
 } 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
@@ -92,15 +85,12 @@ typedef unsigned char uint8_t;
 #define uthash_strlen(s) strlen(s)
 #endif
 
-#ifdef uthash_memcmp
-/* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */
-#warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead"
-#else
-#define uthash_memcmp(a,b,n) memcmp(a,b,n)
+#ifndef HASH_FUNCTION
+#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv)
 #endif
 
 #ifndef HASH_KEYCMP
-#define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n)
+#define HASH_KEYCMP(a,b,n) memcmp(a,b,n)
 #endif
 
 #ifndef uthash_noexpand_fyi
@@ -158,7 +148,7 @@ do {
 
 #define HASH_VALUE(keyptr,keylen,hashv)                                          \
 do {                                                                             \
-  HASH_FCN(keyptr, keylen, hashv);                                               \
+  HASH_FUNCTION(keyptr, keylen, hashv);                                          \
 } while (0)
 
 #define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \
@@ -408,7 +398,7 @@ do {
 do {                                                                             \
   IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \
   (add)->hh.hashv = (hashval);                                                   \
-  (add)->hh.key = (char*) (keyptr);                                              \
+  (add)->hh.key = (const void*) (keyptr);                                        \
   (add)->hh.keylen = (unsigned) (keylen_in);                                     \
   if (!(head)) {                                                                 \
     (add)->hh.next = NULL;                                                       \
@@ -590,13 +580,6 @@ do {
 #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 {                                                                             \
@@ -695,7 +678,8 @@ do {
     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];                                                \
+    case 1:  _hj_i += _hj_key[0];                      /* FALLTHROUGH */         \
+    default: ;                                                                   \
   }                                                                              \
   HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
 } while (0)
@@ -743,6 +727,8 @@ do {
     case 1: hashv += *_sfh_key;                                                  \
             hashv ^= hashv << 10;                                                \
             hashv += hashv >> 1;                                                 \
+            break;                                                               \
+    default: ;                                                                   \
   }                                                                              \
                                                                                  \
   /* Force "avalanching" of final 127 bits */                                    \
@@ -764,7 +750,7 @@ do {
   }                                                                              \
   while ((out) != NULL) {                                                        \
     if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \
-      if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) {              \
+      if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) {                  \
         break;                                                                   \
       }                                                                          \
     }                                                                            \
@@ -850,12 +836,12 @@ do {
   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));            \
+           sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);             \
   if (!_he_new_buckets) {                                                        \
     HASH_RECORD_OOM(oomed);                                                      \
   } else {                                                                       \
     uthash_bzero(_he_new_buckets,                                                \
-        2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket));               \
+        sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);                \
     (tbl)->ideal_chain_maxlen =                                                  \
        ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) +                      \
        ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);    \
@@ -1142,7 +1128,7 @@ typedef struct UT_hash_handle {
    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  */
+   const 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;
diff --git a/src/utlist.h b/src/utlist.h
index 5bb1ac9b7..1979448a7 100644
--- a/src/utlist.h
+++ b/src/utlist.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2007-2018, Troy D. Hanson   http://troydhanson.github.com/uthash/
+Copyright (c) 2007-2021, Troy D. Hanson   http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -24,7 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef UTLIST_H
 #define UTLIST_H
 
-#define UTLIST_VERSION 2.1.0
+#define UTLIST_VERSION 2.3.0
 
 #include <assert.h>
 
diff --git a/src/utringbuffer.h b/src/utringbuffer.h
index ce2890e60..c2b2e673c 100644
--- a/src/utringbuffer.h
+++ b/src/utringbuffer.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2015-2018, Troy D. Hanson   http://troydhanson.github.com/uthash/
+Copyright (c) 2015-2021, Troy D. Hanson   http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef UTRINGBUFFER_H
 #define UTRINGBUFFER_H
 
-#define UTRINGBUFFER_VERSION 2.1.0
+#define UTRINGBUFFER_VERSION 2.3.0
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/utstack.h b/src/utstack.h
index 3b0c1a0df..fc390de10 100644
--- a/src/utstack.h
+++ b/src/utstack.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2018-2018, Troy D. Hanson   http://troydhanson.github.com/uthash/
+Copyright (c) 2018-2021, Troy D. Hanson   http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -24,7 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef UTSTACK_H
 #define UTSTACK_H
 
-#define UTSTACK_VERSION 2.1.0
+#define UTSTACK_VERSION 2.3.0
 
 /*
  * This file contains macros to manipulate a singly-linked list as a stack.
@@ -35,9 +35,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * struct item {
  *      int id;
  *      struct item *next;
- * }
+ * };
  *
- * struct item *stack = NULL:
+ * struct item *stack = NULL;
  *
  * int main() {
  *      int count;
diff --git a/src/utstring.h b/src/utstring.h
index 4cf5ffd3d..f6a33af3e 100644
--- a/src/utstring.h
+++ b/src/utstring.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2018, Troy D. Hanson   http://troydhanson.github.com/uthash/
+Copyright (c) 2008-2021, Troy D. Hanson   http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef UTSTRING_H
 #define UTSTRING_H
 
-#define UTSTRING_VERSION 2.1.0
+#define UTSTRING_VERSION 2.3.0
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/tests/Makefile b/tests/Makefile
index fcb751984..31f0cc240 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -12,7 +12,7 @@ PROGS = test1 test2 test3 test4 test5 test6 test7 test8 test9   \
         test66 test67 test68 test69 test70 test71 test72 test73 \
         test74 test75 test76 test77 test78 test79 test80 test81 \
         test82 test83 test84 test85 test86 test87 test88 test89 \
-        test90 test91 test92 test93 test94 test95
+        test90 test91 test92 test93 test94 test95 test96
 CFLAGS += -I$(HASHDIR)
 #CFLAGS += -DHASH_BLOOM=16
 #CFLAGS += -O2
diff --git a/tests/README b/tests/README
index fc25b9b41..a287de670 100644
--- a/tests/README
+++ b/tests/README
@@ -7,7 +7,7 @@ test2:  make 10-item hash, lookup items with even keys, print
 test3:  make 10-item hash, delete items with even keys, print others
 test4:  10 structs have dual hash handles, separate keys
 test5:  10 structs have dual hash handles, lookup evens by alt key
-test6:  test alt malloc macros (and alt memcmp macro)
+test6:  test alt malloc macros (and alt key-comparison macro)
 test7:  test alt malloc macros with 1000 structs so bucket expansion occurs
 test8:  test num_items counter in UT_hash_handle
 test9:  test "find" after bucket expansion
@@ -89,10 +89,15 @@ test84: test HASH_REPLACE_STR with char* key
 test85: test HASH_OVERHEAD on null and non null hash
 test86: test *_APPEND_ELEM / *_PREPEND_ELEM (Thilo Schulz)
 test87: test HASH_ADD_INORDER() macro (Thilo Schulz)
-test88: test alt memcmp and strlen macros
+test88: test alt key-comparison and strlen macros
 test89: test code from the tinydtls project
 test90: regression-test HASH_ADD_KEYPTR_INORDER (IronBug)
 test91: test LL_INSERT_INORDER etc.
+test92: HASH_NONFATAL_OOM
+test93: alt_fatal
+test94: utlist with fields named other than 'next' and 'prev'
+test95: utstack
+test96: HASH_FUNCTION + HASH_KEYCMP
 
 Other Make targets
 ================================================================================
diff --git a/tests/example.c b/tests/example.c
index a85f422d4..99263027f 100644
--- a/tests/example.c
+++ b/tests/example.c
@@ -1,25 +1,25 @@
-#include <stdio.h>   /* gets */
+#include <stdio.h>   /* printf */
 #include <stdlib.h>  /* atoi, malloc */
 #include <string.h>  /* strcpy */
 #include "uthash.h"
 
 struct my_struct {
     int id;                    /* key */
-    char name[10];
+    char name[21];
     UT_hash_handle hh;         /* makes this structure hashable */
 };
 
 struct my_struct *users = NULL;
 
-void add_user(int user_id, char *name)
+void add_user(int user_id, const char *name)
 {
     struct my_struct *s;
 
     HASH_FIND_INT(users, &user_id, s);  /* id already in the hash? */
-    if (s==NULL) {
-        s = (struct my_struct*)malloc(sizeof(struct my_struct));
+    if (s == NULL) {
+        s = (struct my_struct*)malloc(sizeof *s);
         s->id = user_id;
-        HASH_ADD_INT( users, id, s );  /* id: name of key field */
+        HASH_ADD_INT(users, id, s);  /* id is the key field */
     }
     strcpy(s->name, name);
 }
@@ -28,23 +28,24 @@ struct my_struct *find_user(int user_id)
 {
     struct my_struct *s;
 
-    HASH_FIND_INT( users, &user_id, s );  /* s: output pointer */
+    HASH_FIND_INT(users, &user_id, s);  /* s: output pointer */
     return s;
 }
 
 void delete_user(struct my_struct *user)
 {
-    HASH_DEL( users, user);  /* user: pointer to deletee */
+    HASH_DEL(users, user);  /* user: pointer to deletee */
     free(user);
 }
 
 void delete_all()
 {
-    struct my_struct *current_user, *tmp;
+    struct my_struct *current_user;
+    struct my_struct *tmp;
 
     HASH_ITER(hh, users, current_user, tmp) {
-        HASH_DEL(users,current_user);  /* delete it (users advances to next) */
-        free(current_user);            /* free it */
+        HASH_DEL(users, current_user);  /* delete it (users advances to next) */
+        free(current_user);             /* free it */
     }
 }
 
@@ -52,41 +53,45 @@ void print_users()
 {
     struct my_struct *s;
 
-    for(s=users; s != NULL; s=(struct my_struct*)(s->hh.next)) {
+    for (s = users; s != NULL; s = (struct my_struct*)(s->hh.next)) {
         printf("user id %d: name %s\n", s->id, s->name);
     }
 }
 
-int name_sort(struct my_struct *a, struct my_struct *b)
+int by_name(const struct my_struct *a, const struct my_struct *b)
 {
-    return strcmp(a->name,b->name);
+    return strcmp(a->name, b->name);
 }
 
-int id_sort(struct my_struct *a, struct my_struct *b)
+int by_id(const struct my_struct *a, const struct my_struct *b)
 {
     return (a->id - b->id);
 }
 
-void sort_by_name()
+const char *getl(const char *prompt)
 {
-    HASH_SORT(users, name_sort);
-}
-
-void sort_by_id()
-{
-    HASH_SORT(users, id_sort);
+    static char buf[21];
+    char *p;
+    printf("%s? ", prompt); fflush(stdout);
+    p = fgets(buf, sizeof(buf), stdin);
+    if (p == NULL || (p = strchr(buf, '\n')) == NULL) {
+        puts("Invalid input!");
+        exit(EXIT_FAILURE);
+    }
+    *p = '\0';
+    return buf;
 }
 
 int main()
 {
-    char in[10];
-    int id=1, running=1;
+    int id = 1;
+    int running = 1;
     struct my_struct *s;
-    unsigned num_users;
+    int temp;
 
     while (running) {
         printf(" 1. add user\n");
-        printf(" 2. add/rename user by id\n");
+        printf(" 2. add or rename user by id\n");
         printf(" 3. find user\n");
         printf(" 4. delete user\n");
         printf(" 5. delete all users\n");
@@ -95,27 +100,20 @@ int main()
         printf(" 8. print users\n");
         printf(" 9. count users\n");
         printf("10. quit\n");
-        gets(in);
-        switch(atoi(in)) {
+        switch (atoi(getl("Command"))) {
             case 1:
-                printf("name?\n");
-                add_user(id++, gets(in));
+                add_user(id++, getl("Name (20 char max)"));
                 break;
             case 2:
-                printf("id?\n");
-                gets(in);
-                id = atoi(in);
-                printf("name?\n");
-                add_user(id, gets(in));
+                temp = atoi(getl("ID"));
+                add_user(temp, getl("Name (20 char max)"));
                 break;
             case 3:
-                printf("id?\n");
-                s = find_user(atoi(gets(in)));
+                s = find_user(atoi(getl("ID to find")));
                 printf("user: %s\n", s ? s->name : "unknown");
                 break;
             case 4:
-                printf("id?\n");
-                s = find_user(atoi(gets(in)));
+                s = find_user(atoi(getl("ID to delete")));
                 if (s) {
                     delete_user(s);
                 } else {
@@ -126,20 +124,20 @@ int main()
                 delete_all();
                 break;
             case 6:
-                sort_by_name();
+                HASH_SORT(users, by_name);
                 break;
             case 7:
-                sort_by_id();
+                HASH_SORT(users, by_id);
                 break;
             case 8:
                 print_users();
                 break;
             case 9:
-                num_users=HASH_COUNT(users);
-                printf("there are %u users\n", num_users);
+                temp = HASH_COUNT(users);
+                printf("there are %d users\n", temp);
                 break;
             case 10:
-                running=0;
+                running = 0;
                 break;
         }
     }
diff --git a/tests/hashscan.c b/tests/hashscan.c
index c487d9cee..d18b364ef 100644
--- a/tests/hashscan.c
+++ b/tests/hashscan.c
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005-2018, Troy D. Hanson    http://troydhanson.github.com/uthash/
+Copyright (c) 2005-2021, Troy D. Hanson    http://troydhanson.github.io/uthash/
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/tests/keystats b/tests/keystats
index 9d4076278..9c6bf5b16 100755
--- a/tests/keystats
+++ b/tests/keystats
@@ -12,9 +12,11 @@ sub usage {
 
 usage if ((@ARGV == 0) or ($ARGV[0] eq '-h'));
 
-my @exes = glob "$FindBin::Bin/keystat.???";
+my @exes = glob "'$FindBin::Bin/keystat.???'";
+
 my %stats;
 for my $exe (@exes) {
+    $exe =~ s/\ /\\ /g;
     $stats{$exe} = `$exe @ARGV`;
     delete $stats{$exe} if ($? != 0); # omit hash functions that fail to produce stats (nx)
 }
diff --git a/tests/test10.c b/tests/test10.c
index 64f8035e2..edf80d4a1 100644
--- a/tests/test10.c
+++ b/tests/test10.c
@@ -47,5 +47,8 @@ int main()
     HASH_FIND(alth,altusers,&i,sizeof(int),tmp);
     printf("%d %s in alth\n", i, (tmp != NULL) ? "found" : "not found");
 
+    HASH_CLEAR(hh, users);
+    HASH_CLEAR(alth, altusers);
+
     return 0;
 }
diff --git a/tests/test6.c b/tests/test6.c
index 55ce36bfb..919afa8e1 100644
--- a/tests/test6.c
+++ b/tests/test6.c
@@ -7,15 +7,16 @@
 /* Set up macros for alternative malloc/free functions  */
 #undef uthash_malloc
 #undef uthash_free
-#undef uthash_memcmp
 #undef uthash_strlen
 #undef uthash_bzero
 #define uthash_malloc(sz) alt_malloc(sz)
 #define uthash_free(ptr,sz) alt_free(ptr,sz)
-#define uthash_memcmp(a,b,n) alt_memcmp(a,b,n)
 #define uthash_strlen(s) ..fail_to_compile..
 #define uthash_bzero(a,n) alt_bzero(a,n)
 
+#undef HASH_KEYCMP
+#define HASH_KEYCMP(a,b,n) alt_keycmp(a,b,n)
+
 typedef struct example_user_t {
     int id;
     int cookie;
@@ -41,10 +42,10 @@ static void alt_free(void *ptr, size_t sz)
     free(ptr);
 }
 
-static int alt_memcmp_count = 0;
-static int alt_memcmp(const void *a, const void *b, size_t n)
+static int alt_keycmp_count = 0;
+static int alt_keycmp(const void *a, const void *b, size_t n)
 {
-    ++alt_memcmp_count;
+    ++alt_keycmp_count;
     return memcmp(a,b,n);
 }
 
@@ -115,7 +116,7 @@ int main()
 #else
     assert(alt_bzero_count == 2);
 #endif
-    assert(alt_memcmp_count == 10);
+    assert(alt_keycmp_count == 10);
     assert(alt_malloc_balance == 0);
     return 0;
 }
diff --git a/tests/test65.c b/tests/test65.c
index c863d21db..0584fb1a0 100644
--- a/tests/test65.c
+++ b/tests/test65.c
@@ -3,7 +3,7 @@
 #include "uthash.h"
 
 // this is an example of how to do a LRU cache in C using uthash
-// http://troydhanson.github.com/uthash/
+// http://troydhanson.github.io/uthash/
 // by Jehiah Czebotar 2011 - jehiah@gmail.com
 // this code is in the public domain http://unlicense.org/
 
diff --git a/tests/test76.c b/tests/test76.c
index a18685cae..ee890f6a2 100644
--- a/tests/test76.c
+++ b/tests/test76.c
@@ -8,8 +8,8 @@ int main()
     char V_NeedleStr[] = "needle\0s";
     long *V_KMP_Table;
     long V_FindPos;
-    size_t V_StartPos;
-    size_t V_FindCnt;
+    size_t V_StartPos = 0;
+    size_t V_FindCnt = 0;
 
 
     utstring_new(s);
@@ -24,9 +24,6 @@ int main()
     if (V_KMP_Table != NULL) {
         _utstring_BuildTable(utstring_body(t), utstring_len(t), V_KMP_Table);
 
-        V_FindCnt = 0;
-        V_FindPos = 0;
-        V_StartPos = 0;
         do {
             V_FindPos = _utstring_find(utstring_body(s) + V_StartPos,
                                        utstring_len(s) - V_StartPos,
diff --git a/tests/test77.c b/tests/test77.c
index 7ff97518e..dc55cb244 100644
--- a/tests/test77.c
+++ b/tests/test77.c
@@ -9,7 +9,7 @@ int main()
     long *V_KMP_Table;
     long V_FindPos;
     size_t V_StartPos;
-    size_t V_FindCnt;
+    size_t V_FindCnt = 0;
 
 
     utstring_new(s);
@@ -24,8 +24,6 @@ int main()
     if (V_KMP_Table != NULL) {
         _utstring_BuildTableR(utstring_body(t), utstring_len(t), V_KMP_Table);
 
-        V_FindCnt = 0;
-        V_FindPos = 0;
         V_StartPos = utstring_len(s) - 1;
         do {
             V_FindPos = _utstring_findR(utstring_body(s),
diff --git a/tests/test88.ans b/tests/test88.ans
index 6f35d6e98..7d0129407 100644
--- a/tests/test88.ans
+++ b/tests/test88.ans
@@ -9,22 +9,22 @@ alt_strlen
 alt_strlen
 alt_strlen
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
 alt_strlen
-alt_memcmp
+alt_keycmp
diff --git a/tests/test88.c b/tests/test88.c
index 46f3ee76f..d7a454a0f 100644
--- a/tests/test88.c
+++ b/tests/test88.c
@@ -8,9 +8,9 @@
 
 /* This is mostly a copy of test6.c. */
 
-#undef uthash_memcmp
+#undef HASH_KEYCMP
 #undef uthash_strlen
-#define uthash_memcmp(a,b,n) alt_memcmp(a,b,n)
+#define HASH_KEYCMP(a,b,n) alt_keycmp(a,b,n)
 #define uthash_strlen(s) alt_strlen(s)
 
 typedef struct example_user_t {
@@ -19,9 +19,9 @@ typedef struct example_user_t {
     UT_hash_handle hh;
 } example_user_t;
 
-static int alt_memcmp(const void *a, const void *b, size_t n)
+static int alt_keycmp(const void *a, const void *b, size_t n)
 {
-    puts("alt_memcmp");
+    puts("alt_keycmp");
     return memcmp(a,b,n);
 }
 
diff --git a/tests/test93.c b/tests/test93.c
index 4afe7d51e..81103a1a7 100644
--- a/tests/test93.c
+++ b/tests/test93.c
@@ -39,43 +39,39 @@ static void alt_fatal(char const * s) {
     longjmp(j_buf, 1);
 }
 
-static example_user_t * init_user(int need_malloc_cnt) {
-    users = 0;
+static void init_users(int need_malloc_cnt) {
+    users = NULL;
     example_user_t * user = (example_user_t*)malloc(sizeof(example_user_t));
     user->id = user_id;
     is_fatal = 0;
     malloc_cnt = need_malloc_cnt;
-    /* printf("adding to hash...\n"); */
     if (!setjmp(j_buf)) {
         HASH_ADD_INT(users, id, user);
+    } else {
+        free(user);
     }
-    return user;
 }
 
 int main()
 {
+    example_user_t *user;
 
-#define init(a) do { \
-} while(0)
-
-    example_user_t * user;
-
-    user = init_user(3); /* bloom filter must fail */
+    init_users(3); /* bloom filter must fail */
     if (!is_fatal) {
         printf("fatal not called after bloom failure\n");
     }
 
-    user = init_user(2); /* bucket creation must fail */
+    init_users(2); /* bucket creation must fail */
     if (!is_fatal) {
         printf("fatal not called after bucket creation failure\n");
     }
 
-    user = init_user(1); /* table creation must fail */
+    init_users(1); /* table creation must fail */
     if (!is_fatal) {
         printf("fatal not called after table creation failure\n");
     }
 
-    user = init_user(4); /* hash must create OK */
+    init_users(4); /* hash must create OK */
     if (is_fatal) {
         printf("fatal error when creating hash normally\n");
         /* bad idea to continue running */
@@ -83,19 +79,20 @@ int main()
     }
 
     /* let's add users until expansion fails */
-    users = 0;
+    users = NULL;
     malloc_cnt = 4;
     while (1) {
-        user = (example_user_t*)malloc(sizeof(example_user_t));
-        user->id = user_id;
         if (user_id++ == 1000) {
             printf("there is no way 1000 iterations didn't require realloc\n");
             break;
         }
+        user = (example_user_t*)malloc(sizeof(example_user_t));
+        user->id = user_id;
         if (!setjmp(j_buf)) {
             HASH_ADD_INT(users, id, user);
+        } else {
+            free(user);
         }
-        malloc_cnt = 0;
         if (malloc_failed) {
 
             if (!is_fatal) {
@@ -108,12 +105,12 @@ int main()
             /* we can't really do anything, the hash is not in consistent
              * state, so assume this is a success. */
             break;
-
         }
+        malloc_cnt = 0;
     }
 
-    printf("End\n");
+    HASH_CLEAR(hh, users);
 
+    printf("End\n");
     return 0;
-
 }
diff --git a/tests/test96.ans b/tests/test96.ans
new file mode 100644
index 000000000..64556d9f8
--- /dev/null
+++ b/tests/test96.ans
@@ -0,0 +1,40 @@
+time 56 not found, inserting it
+time 7 not found, inserting it
+time 10 not found, inserting it
+time 39 not found, inserting it
+time 82 found with value 10
+time 15 found with value 39
+time 31 found with value 7
+time 26 not found, inserting it
+time 51 found with value 39
+time 83 not found, inserting it
+time 46 found with value 10
+time 92 found with value 56
+time 49 not found, inserting it
+time 25 found with value 49
+time 80 found with value 56
+time 54 not found, inserting it
+time 97 found with value 49
+time 9 not found, inserting it
+time 34 found with value 10
+time 86 found with value 26
+time 87 found with value 39
+time 28 not found, inserting it
+time 13 found with value 49
+time 91 found with value 7
+time 95 found with value 83
+time 63 found with value 39
+time 71 found with value 83
+time 100 found with value 28
+time 44 found with value 56
+time 42 found with value 54
+time 16 found with value 28
+time 32 found with value 56
+time 6 found with value 54
+time 85 found with value 49
+time 40 found with value 28
+time 20 found with value 56
+time 18 found with value 54
+time 99 found with value 39
+time 22 found with value 10
+time 1 found with value 49
diff --git a/tests/test96.c b/tests/test96.c
new file mode 100644
index 000000000..700bdcf30
--- /dev/null
+++ b/tests/test96.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define HASH_FUNCTION(a,n,hv) (hv = clockface_hash(*(const int*)(a)))
+#define HASH_KEYCMP(a,b,n) clockface_neq(*(const int*)(a), *(const int*)(b))
+
+#include "uthash.h"
+
+struct clockface {
+    int time;
+    UT_hash_handle hh;
+};
+
+int clockface_hash(int time)
+{
+    return (time % 4);
+}
+
+int clockface_neq(int t1, int t2)
+{
+    return ((t1 % 12) != (t2 % 12));
+}
+
+int main()
+{
+    int random_data[] = {
+        56, 7, 10, 39, 82, 15, 31, 26, 51, 83,
+        46, 92, 49, 25, 80, 54, 97, 9, 34, 86,
+        87, 28, 13, 91, 95, 63, 71, 100, 44, 42,
+        16, 32, 6, 85, 40, 20, 18, 99, 22, 1
+    };
+
+    struct clockface *times = NULL;
+    for (int i=0; i < 40; ++i) {
+        struct clockface *elt = (struct clockface *)malloc(sizeof(*elt));
+        struct clockface *found = NULL;
+        elt->time = random_data[i];
+        HASH_FIND_INT(times, &elt->time, found);
+        if (found) {
+            printf("time %d found with value %d\n", elt->time, found->time);
+        } else {
+            printf("time %d not found, inserting it\n", elt->time);
+            HASH_ADD_INT(times, time, elt);
+        }
+    }
+
+    return 0;
+}
-- 
cgit v1.2.3