--- /dev/null
+/*
+ * str.h
+ *
+ * Copyright (c) 2019, Urban Wallasch
+ * BSD 3-Clause License, see LICENSE file for more details.
+ *
+ * Collection of inline functions to complement string.h
+ *
+ * str_icmp,
+ * str_nicmp - compare two strings ignoring case
+ *
+ * str_istr - locate a substring ignoring case
+ *
+ * mem_mem,
+ * mem_str,
+ * mem_istr - locate a pattern in memory
+ *
+ */
+
+#ifndef STR_H_
+#define STR_H_
+
+#ifdef cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+
+/*
+ * str_icmp, str_nicmp - compare two strings ignoring case
+ *
+ * The strc_icmp() function performs a byte-by-byte comparison of the
+ * strings s1 and s2, ignoring the case of the characters.
+ *
+ * The str_nicmp() function is similar, except that it compares no more
+ * than n bytes of s1 and s2. If n is zero, the return value is zero.
+ *
+ * The strc_icmp() and str_nicmp() functions return an integer less than,
+ * equal to, or greater than zero if s1 is, after ignoring case, found to
+ * be less than, to match, or be greater than s2, respectively.
+*/
+static inline int str_icmp(const char *s1, const char *s2) {
+ int r;
+
+ do {
+ r = tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
+ }
+ while (r == 0 && *s1++ && *s2++);
+ return r;
+}
+
+static inline int str_nicmp(const char *s1, const char *s2, size_t n) {
+ int r = 0;
+
+ if (n > 0) {
+ do {
+ r = tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
+ }
+ while (r == 0 && --n && *s1++ && *s2++);
+ }
+ return r;
+}
+
+
+/*
+ * str_istr - locate a substring ignoring case
+ *
+ * The str_istr() function finds the first occurrence of the substring
+ * needle (excluding terminating null byte) in the string haystack,
+ * ignoring the case of both arguments.
+ *
+ * The function returns a pointer to the beginning of the located
+ * substring, or NULL if the substring is not found. If needle points
+ * to a string with zero length, the function returns haystack.
+ */
+static inline char *str_istr(const char *haystack, const char *needle) {
+ size_t n = needle ? strlen(needle) : 0;
+
+ do {
+ if (str_nicmp(haystack, needle, n) == 0)
+ return (char *)haystack;
+ } while (*haystack++);
+ return NULL;
+}
+
+
+/*
+ * mem_mem, mem_str, mem_istr - locate a pattern in memory
+ *
+ * The mem_mem() function finds the first occurrence of the pattern
+ * needle of length n in an haystack of size h.
+ *
+ * The mem_str() function is similar, except that it expects a string
+ * for needle, ignoring the terminating null character. The mem_istr()
+ * function additionally ignores the case of characters in both needle
+ * and haystack.
+ *
+ * The mem_mem(), mem_str() and mem_istr() functions return a pointer
+ * to the beginning of the located pattern, or NULL if the pattern is
+ * not found. If the search pattern is of zero length, the functions
+ * return haystack.
+ */
+static inline void *mem_mem(const void *haystack, size_t h, const void *needle, size_t n) {
+ if (h >= n) {
+ const char *p = haystack;
+ h -= n;
+ do {
+ if (memcmp(p, needle, n) == 0)
+ return (void *)p;
+ ++p;
+ } while (h--);
+ }
+ return NULL;
+}
+
+static inline void *mem_str(const void *haystack, size_t h, const char *needle) {
+ return mem_mem(haystack, h, needle, needle ? strlen(needle) : 0);
+}
+
+static inline void *mem_istr(const void *haystack, size_t h, const char *needle) {
+ size_t n = needle ? strlen(needle) : 0;
+
+ if (h >= n) {
+ const char *p = haystack;
+ h -= n;
+ do {
+ if (str_nicmp(p, needle, n) == 0)
+ return (void *)p;
+ ++p;
+ } while (h--);
+ }
+ return NULL;
+}
+
+#ifdef cplusplus
+}
+#endif
+
+#endif /*ndef STR_H_*/
--- /dev/null
+/*
+ * str_test.c
+ *
+ * Copyright (c) 2019, Urban Wallasch
+ * BSD 3-Clause License, see LICENSE file for more details.
+ *
+ * Simple, non-exhaustive quick check for str.h.
+ *
+ * Build with:
+ * gcc -std=c99 -Wall -Wextra -Wpedantic -Werror -O2 -ostr_test str_test.c
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "str.h"
+
+
+#define H(a_) fprintf(stderr, "\n%s\n", a_)
+
+#define D(s_) do{ \
+ fprintf(stderr, ". %s = '", #s_); \
+ for (int i = 0; i < (int)sizeof(s_); ++i) { \
+ if (s_[i] == 0) \
+ fprintf(stderr, "\\0"); \
+ else if (s_[i] < ' ' || s_[i] > 127) \
+ fprintf(stderr, "\\x%02"PRIX8, (uint8_t)(s_[i])); \
+ else \
+ fputc(s_[i], stderr); \
+ } \
+ fprintf(stderr, "'\n"); \
+ }while(0) \
+
+
+#define T(c_) do{ \
+ assert(c_); \
+ fprintf(stderr," %3d: %s \t pass\n",__LINE__,#c_); \
+ }while(0)
+
+
+int main(void) {
+
+ H("str_icmp");
+ T(str_icmp("asdf", "") > 0);
+ T(str_icmp("", "asdf") < 0);
+ T(str_icmp("asdf", "b") < 0);
+ T(str_icmp("b", "asdf") > 0);
+ T(str_icmp("asdfまほ", "asdfまほ") == 0);
+ T(str_icmp("Asdf", "asdf") == 0);
+ T(str_icmp("まほaSdf", "まほasdF") == 0);
+ T(str_icmp("asdf", "asdF") == 0);
+
+ H("str_nicmp");
+ T(str_nicmp(NULL, NULL, 0) == 0);
+ T(str_nicmp(NULL, "x", 0) == 0);
+ T(str_nicmp("x", NULL, 0) == 0);
+ T(str_nicmp("asdf", NULL, 0) == 0);
+ T(str_nicmp("asdf", NULL, 0) == 0);
+ T(str_nicmp("asdf", "", 0) == 0);
+ T(str_nicmp("asdf", "a", 1) == 0);
+ T(str_nicmp("a", "asdf", 1) == 0);
+ T(str_nicmp("asdf", "ab", 1) == 0);
+ T(str_nicmp("ab", "asdf", 1) == 0);
+ T(str_nicmp("asdf", "ab", 2) > 0);
+ T(str_nicmp("ab", "asdf", 2) < 0);
+ T(str_nicmp("asdfまほ", "asdまほ", 1) == 0);
+ T(str_nicmp("Asdf", "asdf", 1) == 0);
+ T(str_nicmp("まほaSdf", "まほasdF", 1) == 0);
+ T(str_nicmp("asdf", "asdF", 1) == 0);
+
+ H("str_istr");
+ char h1[] = "asdfま\0ほyxc";
+ char h2[] = "";
+ D(h1);
+ D(h2);
+ T(str_istr(NULL, NULL) == NULL);
+ T(str_istr(h2, NULL) == h2);
+ T(str_istr(NULL, "") == NULL);
+ T(str_istr(h2, "") == h2);
+ T(str_istr(h1, "") == h1);
+ T(str_istr(h2, "\0\0") == h2);
+ T(str_istr("", "a") == NULL);
+ T(str_istr(h1, "xoxoxoxo") == NULL);
+ T(str_istr(h1, "S") == h1 + 1);
+ T(str_istr(h1, "as") == h1);
+ T(str_istr(h1, "df") == h1 + 2);
+ T(str_istr(h1, "As") == h1);
+ T(str_istr(h1, "dF") == h1 + 2);
+ T(str_istr(h1, "X") == NULL);
+ T(str_istr(h1, "yxc") == NULL);
+
+ H("mem_mem");
+ char m1[] = { 1,'s','d','f', 0,'y','x','c', -1 };
+ char m2[] = { 'a','b','c', 0 };
+ D(m1);
+ D(m2);
+ T(mem_mem(NULL, 0, NULL, 0) == NULL);
+ T(mem_mem(m2, 0, NULL, 0) == m2);
+ T(mem_mem(m2, 0, "", 0) == m2);
+ T(mem_mem(NULL, 0, m2, 0) == NULL);
+ T(mem_mem(NULL, 0, "", 0) == NULL);
+ T(mem_mem(m2, 3, "abc", 3) == m2);
+ T(mem_mem(m2, 4, "abc", 4) == m2);
+ T(mem_mem(m1, sizeof m1, "", 0) == m1);
+ T(mem_mem(m1, sizeof m1, NULL, 0) == m1);
+ T(mem_mem(m1, sizeof m1, "\x01s", 2) == m1);
+ T(mem_mem(m1, sizeof m1, "f\0y", 3) == m1 + 3);
+ T(mem_mem(m1, sizeof m1, "f\0y" + 1, 2) == m1 + 4);
+ T(mem_mem(m1, sizeof m1, "f\0Y" + 1, 2) == NULL);
+ T(mem_mem(m1, sizeof m1, (char[]){-1}, 1) == m1 + 8);
+ T(mem_mem(m1, sizeof m1, "xc", 3) == NULL);
+ T(mem_mem(m1, sizeof m1, "xc", 2) == m1 + 6);
+
+ H("mem_str");
+ D(m1);
+ T(mem_str(NULL, 0, NULL) == NULL);
+ T(mem_str(NULL, 0, "") == NULL);
+ T(mem_str(m1, 0, NULL) == m1);
+ T(mem_str(m1, 0, "") == m1);
+ T(mem_str(m1, sizeof m1, "") == m1);
+ T(mem_str(m1, sizeof m1, NULL) == m1);
+ T(mem_str(m1, sizeof m1, "sdf") == m1 + 1);
+ T(mem_str(m1, sizeof m1, "yxc") == m1 + 5);
+ T(mem_str(m1, sizeof m1, "yxc\0qwe") == m1 + 5);
+ T(mem_str(m1, sizeof m1, "yXc\0qwe") == NULL);
+
+ H("mem_istr");
+ D(m1);
+ T(mem_istr(NULL, 0, NULL) == NULL);
+ T(mem_istr(NULL, 0, "") == NULL);
+ T(mem_istr(m1, 0, NULL) == m1);
+ T(mem_istr(m1, 0, "") == m1);
+ T(mem_istr(m1, sizeof m1, "") == m1);
+ T(mem_istr(m1, sizeof m1, NULL) == m1);
+ T(mem_istr(m1, sizeof m1, "") == m1);
+ T(mem_istr(m1, sizeof m1, "sDf") == m1 + 1);
+ T(mem_istr(m1, sizeof m1, "Yxc") == m1 + 5);
+ T(mem_istr(m1, sizeof m1, "yxC\0qwe") == m1 + 5);
+
+ H("All tests passed.");
+ return 0;
+}