Blob Blame History Raw
From 9a99549f5df6ef69dd1d2b509c13aaff4e431742 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 19 May 2020 16:11:28 +0100
Subject: [PATCH 19/19] common/include: Add locale-safe ascii_strcasecmp and
 ascii_strncasecmp.

These are derived from the FreeBSD functions here:
https://github.com/freebsd/freebsd/blob/master/sys/libkern/strcasecmp.c

Thanks: Eric Blake.
(cherry picked from commit 46a29b8e91d69e812d78df53e91b526a560000fe)
---
 .gitignore                          |  1 +
 common/include/Makefile.am          |  6 +++
 common/include/ascii-ctype.h        |  6 +++
 common/include/ascii-string.h       | 77 ++++++++++++++++++++++++++++
 common/include/test-ascii-string.c  | 79 +++++++++++++++++++++++++++++
 plugins/curl/curl.c                 |  7 +--
 plugins/info/info.c                 | 17 ++++---
 plugins/nbd/nbd.c                   |  8 +--
 plugins/partitioning/partitioning.c | 10 ++--
 plugins/sh/call.c                   | 25 ++++-----
 server/main.c                       |  8 +--
 server/public.c                     | 21 ++++----
 12 files changed, 222 insertions(+), 43 deletions(-)
 create mode 100644 common/include/ascii-string.h
 create mode 100644 common/include/test-ascii-string.c

diff --git a/.gitignore b/.gitignore
index 523894b7..aa148f04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@ Makefile.in
 /autom4te.cache
 /common/bitmap/test-bitmap
 /common/include/test-ascii-ctype
+/common/include/test-ascii-string
 /common/include/test-byte-swapping
 /common/include/test-current-dir-name
 /common/include/test-isaligned
diff --git a/common/include/Makefile.am b/common/include/Makefile.am
index d7b0d7a8..eff71863 100644
--- a/common/include/Makefile.am
+++ b/common/include/Makefile.am
@@ -35,6 +35,7 @@ include $(top_srcdir)/common-rules.mk
 # plugins and/or filters.  They are not installed.
 EXTRA_DIST = \
 	ascii-ctype.h \
+	ascii-string.h \
 	byte-swapping.h \
 	exit-with-parent.h \
 	get-current-dir-name.h \
@@ -52,6 +53,7 @@ EXTRA_DIST = \
 
 TESTS = \
 	test-ascii-ctype \
+	test-ascii-string \
 	test-byte-swapping \
 	test-current-dir-name \
 	test-isaligned \
@@ -68,6 +70,10 @@ test_ascii_ctype_SOURCES = test-ascii-ctype.c ascii-ctype.h
 test_ascii_ctype_CPPFLAGS = -I$(srcdir)
 test_ascii_ctype_CFLAGS = $(WARNINGS_CFLAGS)
 
+test_ascii_string_SOURCES = test-ascii-string.c ascii-string.h
+test_ascii_string_CPPFLAGS = -I$(srcdir)
+test_ascii_string_CFLAGS = $(WARNINGS_CFLAGS)
+
 test_byte_swapping_SOURCES = test-byte-swapping.c byte-swapping.h
 test_byte_swapping_CPPFLAGS = -I$(srcdir)
 test_byte_swapping_CFLAGS = $(WARNINGS_CFLAGS)
diff --git a/common/include/ascii-ctype.h b/common/include/ascii-ctype.h
index 5e8bf237..a700563e 100644
--- a/common/include/ascii-ctype.h
+++ b/common/include/ascii-ctype.h
@@ -49,6 +49,9 @@
 #define ascii_isspace(c)                                                \
   ((c) == '\t' || (c) == '\n' || (c) == '\f' || (c) == '\r' || (c) == ' ')
 
+#define ascii_isupper(c)                        \
+  ((c) >= 'A' && (c) <= 'Z')
+
 #define ascii_isxdigit(c)                                               \
   ((c) == '0' || (c) == '1' || (c) == '2' || (c) == '3' || (c) == '4' || \
    (c) == '5' || (c) == '6' || (c) == '7' || (c) == '8' || (c) == '9' || \
@@ -57,4 +60,7 @@
    (c) == 'A' || (c) == 'B' || (c) == 'C' ||                            \
    (c) == 'D' || (c) == 'E' || (c) == 'F')
 
+#define ascii_tolower(c)                        \
+  (ascii_isupper ((c)) ? (c) - 'A' + 'a' : (c))
+
 #endif /* NBDKIT_ASCII_CTYPE_H */
diff --git a/common/include/ascii-string.h b/common/include/ascii-string.h
new file mode 100644
index 00000000..0a60d5f4
--- /dev/null
+++ b/common/include/ascii-string.h
@@ -0,0 +1,77 @@
+/* nbdkit
+ * Copyright (C) 2013-2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT 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 RED HAT 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.
+ */
+
+/* Case insensitive string comparison functions (like strcasecmp,
+ * strncasecmp) which work correctly in any locale.  They can only be
+ * used for comparison when one or both strings is 7 bit ASCII.
+ */
+
+#ifndef NBDKIT_ASCII_STRING_H
+#define NBDKIT_ASCII_STRING_H
+
+#include "ascii-ctype.h"
+
+static inline int
+ascii_strcasecmp (const char *s1, const char *s2)
+{
+  const unsigned char *us1 = (const unsigned char *)s1;
+  const unsigned char *us2 = (const unsigned char *)s2;
+
+  while (ascii_tolower (*us1) == ascii_tolower (*us2)) {
+    if (*us1++ == '\0')
+      return 0;
+    us2++;
+  }
+
+  return ascii_tolower (*us1) - ascii_tolower (*us2);
+}
+
+static inline int
+ascii_strncasecmp (const char *s1, const char *s2, size_t n)
+{
+  if (n != 0) {
+    const unsigned char *us1 = (const unsigned char *)s1;
+    const unsigned char *us2 = (const unsigned char *)s2;
+
+    do {
+      if (ascii_tolower (*us1) != ascii_tolower (*us2))
+        return ascii_tolower (*us1) - ascii_tolower (*us2);
+      if (*us1++ == '\0')
+        break;
+      us2++;
+    } while (--n != 0);
+  }
+
+  return 0;
+}
+
+#endif /* NBDKIT_ASCII_STRING_H */
diff --git a/common/include/test-ascii-string.c b/common/include/test-ascii-string.c
new file mode 100644
index 00000000..0fa4a483
--- /dev/null
+++ b/common/include/test-ascii-string.c
@@ -0,0 +1,79 @@
+/* nbdkit
+ * Copyright (C) 2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT 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 RED HAT 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ascii-string.h"
+
+int
+main (void)
+{
+  assert (ascii_strcasecmp ("", "") == 0);
+  assert (ascii_strcasecmp ("a", "a") == 0);
+  assert (ascii_strcasecmp ("abc", "abc") == 0);
+  assert (ascii_strcasecmp ("a", "b") < 0);
+  assert (ascii_strcasecmp ("b", "a") > 0);
+  assert (ascii_strcasecmp ("aa", "a") > 0);
+
+  /* Second string contains Turkish dotless lowercase letter 覺. */
+  assert (ascii_strcasecmp ("hi", "h覺") != 0);
+
+  /* Check that we got our rounding behaviour correct. */
+  assert (ascii_strcasecmp ("\x1", "\x7f") < 0);
+  assert (ascii_strcasecmp ("\x1", "\x80") < 0);
+  assert (ascii_strcasecmp ("\x1", "\x81") < 0);
+  assert (ascii_strcasecmp ("\x1", "\xff") < 0);
+
+  assert (ascii_strncasecmp ("", "", 0) == 0);
+  assert (ascii_strncasecmp ("a", "a", 1) == 0);
+  assert (ascii_strncasecmp ("abc", "abc", 3) == 0);
+  assert (ascii_strncasecmp ("abc", "def", 0) == 0);
+  assert (ascii_strncasecmp ("abc", "abd", 2) == 0);
+  assert (ascii_strncasecmp ("a", "b", 1) < 0);
+  assert (ascii_strncasecmp ("b", "a", 1) > 0);
+  assert (ascii_strncasecmp ("aa", "a", 2) > 0);
+  assert (ascii_strncasecmp ("aa", "a", 100) > 0);
+
+  assert (ascii_strncasecmp ("hi", "h覺", 1) == 0);
+  assert (ascii_strncasecmp ("hi", "h覺", 2) != 0);
+
+  assert (ascii_strncasecmp ("\x1", "\x7f", 1) < 0);
+  assert (ascii_strncasecmp ("\x1", "\x80", 1) < 0);
+  assert (ascii_strncasecmp ("\x1", "\x81", 1) < 0);
+  assert (ascii_strncasecmp ("\x1", "\xff", 1) < 0);
+
+  exit (EXIT_SUCCESS);
+}
diff --git a/plugins/curl/curl.c b/plugins/curl/curl.c
index ac30cbdd..00f0628a 100644
--- a/plugins/curl/curl.c
+++ b/plugins/curl/curl.c
@@ -58,6 +58,7 @@
 
 #include "cleanup.h"
 #include "ascii-ctype.h"
+#include "ascii-string.h"
 
 static const char *url = NULL;
 static const char *user = NULL;
@@ -419,8 +420,8 @@ curl_open (int readonly)
 #endif
   nbdkit_debug ("content length: %" PRIi64, h->exportsize);
 
-  if (strncasecmp (url, "http://", strlen ("http://")) == 0 ||
-      strncasecmp (url, "https://", strlen ("https://")) == 0) {
+  if (ascii_strncasecmp (url, "http://", strlen ("http://")) == 0 ||
+      ascii_strncasecmp (url, "https://", strlen ("https://")) == 0) {
     if (!h->accept_range) {
       nbdkit_error ("server does not support 'range' (byte range) requests");
       goto err;
@@ -504,7 +505,7 @@ header_cb (void *ptr, size_t size, size_t nmemb, void *opaque)
   const char *bytes = "bytes";
 
   if (realsize >= strlen (accept_ranges) &&
-      strncasecmp (header, accept_ranges, strlen (accept_ranges)) == 0) {
+      ascii_strncasecmp (header, accept_ranges, strlen (accept_ranges)) == 0) {
     const char *p = strchr (header, ':') + 1;
 
     /* Skip whitespace between the header name and value. */
diff --git a/plugins/info/info.c b/plugins/info/info.c
index 329a3684..e04d672b 100644
--- a/plugins/info/info.c
+++ b/plugins/info/info.c
@@ -49,6 +49,7 @@
 
 #include <nbdkit-plugin.h>
 
+#include "ascii-string.h"
 #include "byte-swapping.h"
 #include "tvdiff.h"
 
@@ -76,12 +77,12 @@ static int
 info_config (const char *key, const char *value)
 {
   if (strcmp (key, "mode") == 0) {
-    if (strcasecmp (value, "exportname") == 0 ||
-        strcasecmp (value, "export-name") == 0) {
+    if (ascii_strcasecmp (value, "exportname") == 0 ||
+        ascii_strcasecmp (value, "export-name") == 0) {
       mode = MODE_EXPORTNAME;
     }
-    else if (strcasecmp (value, "base64exportname") == 0 ||
-             strcasecmp (value, "base64-export-name") == 0) {
+    else if (ascii_strcasecmp (value, "base64exportname") == 0 ||
+             ascii_strcasecmp (value, "base64-export-name") == 0) {
 #ifdef HAVE_BASE64
       mode = MODE_BASE64EXPORTNAME;
 #else
@@ -89,13 +90,13 @@ info_config (const char *key, const char *value)
       return -1;
 #endif
     }
-    else if (strcasecmp (value, "address") == 0)
+    else if (ascii_strcasecmp (value, "address") == 0)
       mode = MODE_ADDRESS;
-    else if (strcasecmp (value, "time") == 0)
+    else if (ascii_strcasecmp (value, "time") == 0)
       mode = MODE_TIME;
-    else if (strcasecmp (value, "uptime") == 0)
+    else if (ascii_strcasecmp (value, "uptime") == 0)
       mode = MODE_UPTIME;
-    else if (strcasecmp (value, "conntime") == 0)
+    else if (ascii_strcasecmp (value, "conntime") == 0)
       mode = MODE_CONNTIME;
     else {
       nbdkit_error ("unknown mode: '%s'", value);
diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c
index d020beec..980ce8ec 100644
--- a/plugins/nbd/nbd.c
+++ b/plugins/nbd/nbd.c
@@ -52,6 +52,8 @@
 #define NBDKIT_API_VERSION 2
 
 #include <nbdkit-plugin.h>
+
+#include "ascii-string.h"
 #include "byte-swapping.h"
 #include "cleanup.h"
 #include "utils.h"
@@ -152,9 +154,9 @@ nbdplug_config (const char *key, const char *value)
     shared = r;
   }
   else if (strcmp (key, "tls") == 0) {
-    if (strcasecmp (value, "require") == 0 ||
-        strcasecmp (value, "required") == 0 ||
-        strcasecmp (value, "force") == 0)
+    if (ascii_strcasecmp (value, "require") == 0 ||
+        ascii_strcasecmp (value, "required") == 0 ||
+        ascii_strcasecmp (value, "force") == 0)
       tls = LIBNBD_TLS_REQUIRE;
     else {
       r = nbdkit_parse_bool (value);
diff --git a/plugins/partitioning/partitioning.c b/plugins/partitioning/partitioning.c
index 6e426b93..e35764dc 100644
--- a/plugins/partitioning/partitioning.c
+++ b/plugins/partitioning/partitioning.c
@@ -48,6 +48,7 @@
 
 #include <nbdkit-plugin.h>
 
+#include "ascii-string.h"
 #include "byte-swapping.h"
 #include "isaligned.h"
 #include "iszero.h"
@@ -176,9 +177,10 @@ partitioning_config (const char *key, const char *value)
     nr_files++;
   }
   else if (strcmp (key, "partition-type") == 0) {
-    if (strcasecmp (value, "mbr") == 0 || strcasecmp (value, "dos") == 0)
+    if (ascii_strcasecmp (value, "mbr") == 0 ||
+        ascii_strcasecmp (value, "dos") == 0)
       parttype = PARTTYPE_MBR;
-    else if (strcasecmp (value, "gpt") == 0)
+    else if (ascii_strcasecmp (value, "gpt") == 0)
       parttype = PARTTYPE_GPT;
     else {
       nbdkit_error ("unknown partition-type: %s", value);
@@ -209,13 +211,13 @@ partitioning_config (const char *key, const char *value)
     alignment = r;
   }
   else if (strcmp (key, "mbr-id") == 0) {
-    if (strcasecmp (value, "default") == 0)
+    if (ascii_strcasecmp (value, "default") == 0)
       mbr_id = DEFAULT_MBR_ID;
     else if (nbdkit_parse_uint8_t ("mbr-id", value, &mbr_id) == -1)
       return -1;
   }
   else if (strcmp (key, "type-guid") == 0) {
-    if (strcasecmp (value, "default") == 0)
+    if (ascii_strcasecmp (value, "default") == 0)
       parse_guid (DEFAULT_TYPE_GUID, type_guid);
     else if (parse_guid (value, type_guid) == -1) {
       nbdkit_error ("could not validate GUID: %s", value);
diff --git a/plugins/sh/call.c b/plugins/sh/call.c
index ba9f055f..554e2f78 100644
--- a/plugins/sh/call.c
+++ b/plugins/sh/call.c
@@ -48,6 +48,7 @@
 #include <nbdkit-plugin.h>
 
 #include "ascii-ctype.h"
+#include "ascii-string.h"
 #include "cleanup.h"
 #include "utils.h"
 
@@ -332,48 +333,48 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
   }
 
   /* Recognize the errno values that match NBD protocol errors */
-  if (strncasecmp (ebuf, "EPERM", 5) == 0) {
+  if (ascii_strncasecmp (ebuf, "EPERM", 5) == 0) {
     err = EPERM;
     skip = 5;
   }
-  else if (strncasecmp (ebuf, "EIO", 3) == 0) {
+  else if (ascii_strncasecmp (ebuf, "EIO", 3) == 0) {
     err = EIO;
     skip = 3;
   }
-  else if (strncasecmp (ebuf, "ENOMEM", 6) == 0) {
+  else if (ascii_strncasecmp (ebuf, "ENOMEM", 6) == 0) {
     err = ENOMEM;
     skip = 6;
   }
-  else if (strncasecmp (ebuf, "EINVAL", 6) == 0) {
+  else if (ascii_strncasecmp (ebuf, "EINVAL", 6) == 0) {
     err = EINVAL;
     skip = 6;
   }
-  else if (strncasecmp (ebuf, "ENOSPC", 6) == 0) {
+  else if (ascii_strncasecmp (ebuf, "ENOSPC", 6) == 0) {
     err = ENOSPC;
     skip = 6;
   }
-  else if (strncasecmp (ebuf, "EOVERFLOW", 9) == 0) {
+  else if (ascii_strncasecmp (ebuf, "EOVERFLOW", 9) == 0) {
     err = EOVERFLOW;
     skip = 9;
   }
-  else if (strncasecmp (ebuf, "ESHUTDOWN", 9) == 0) {
+  else if (ascii_strncasecmp (ebuf, "ESHUTDOWN", 9) == 0) {
     err = ESHUTDOWN;
     skip = 9;
   }
-  else if (strncasecmp (ebuf, "ENOTSUP", 7) == 0) {
+  else if (ascii_strncasecmp (ebuf, "ENOTSUP", 7) == 0) {
     err = ENOTSUP;
     skip = 7;
   }
-  else if (strncasecmp (ebuf, "EOPNOTSUPP", 10) == 0) {
+  else if (ascii_strncasecmp (ebuf, "EOPNOTSUPP", 10) == 0) {
     err = EOPNOTSUPP;
     skip = 10;
   }
   /* Other errno values that server/protocol.c treats specially */
-  else if (strncasecmp (ebuf, "EROFS", 5) == 0) {
+  else if (ascii_strncasecmp (ebuf, "EROFS", 5) == 0) {
     err = EROFS;
     skip = 5;
   }
-  else if (strncasecmp (ebuf, "EDQUOT", 6) == 0) {
+  else if (ascii_strncasecmp (ebuf, "EDQUOT", 6) == 0) {
 #ifdef EDQUOT
     err = EDQUOT;
 #else
@@ -381,7 +382,7 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
 #endif
     skip = 6;
   }
-  else if (strncasecmp (ebuf, "EFBIG", 5) == 0) {
+  else if (ascii_strncasecmp (ebuf, "EFBIG", 5) == 0) {
     err = EFBIG;
     skip = 5;
   }
diff --git a/server/main.c b/server/main.c
index 11ba1e6d..08116d74 100644
--- a/server/main.c
+++ b/server/main.c
@@ -55,6 +55,8 @@
 
 #include <dlfcn.h>
 
+#include "ascii-string.h"
+
 #include "internal.h"
 #include "nbd-protocol.h"
 #include "options.h"
@@ -282,9 +284,9 @@ main (int argc, char *argv[])
 
     case TLS_OPTION:
       tls_set_on_cli = true;
-      if (strcasecmp (optarg, "require") == 0 ||
-          strcasecmp (optarg, "required") == 0 ||
-          strcasecmp (optarg, "force") == 0)
+      if (ascii_strcasecmp (optarg, "require") == 0 ||
+          ascii_strcasecmp (optarg, "required") == 0 ||
+          ascii_strcasecmp (optarg, "force") == 0)
         tls = 2;
       else {
         tls = nbdkit_parse_bool (optarg);
diff --git a/server/public.c b/server/public.c
index 98b78482..919f082d 100644
--- a/server/public.c
+++ b/server/public.c
@@ -52,6 +52,7 @@
 #include <sys/socket.h>
 
 #include "ascii-ctype.h"
+#include "ascii-string.h"
 #include "get-current-dir-name.h"
 
 #include "internal.h"
@@ -385,19 +386,19 @@ int
 nbdkit_parse_bool (const char *str)
 {
   if (!strcmp (str, "1") ||
-      !strcasecmp (str, "true") ||
-      !strcasecmp (str, "t") ||
-      !strcasecmp (str, "yes") ||
-      !strcasecmp (str, "y") ||
-      !strcasecmp (str, "on"))
+      !ascii_strcasecmp (str, "true") ||
+      !ascii_strcasecmp (str, "t") ||
+      !ascii_strcasecmp (str, "yes") ||
+      !ascii_strcasecmp (str, "y") ||
+      !ascii_strcasecmp (str, "on"))
     return 1;
 
   if (!strcmp (str, "0") ||
-      !strcasecmp (str, "false") ||
-      !strcasecmp (str, "f") ||
-      !strcasecmp (str, "no") ||
-      !strcasecmp (str, "n") ||
-      !strcasecmp (str, "off"))
+      !ascii_strcasecmp (str, "false") ||
+      !ascii_strcasecmp (str, "f") ||
+      !ascii_strcasecmp (str, "no") ||
+      !ascii_strcasecmp (str, "n") ||
+      !ascii_strcasecmp (str, "off"))
     return 0;
 
   nbdkit_error ("could not decipher boolean (%s)", str);
-- 
2.18.2