ctype: new compat module
authorMarko Kreen <markokr@gmail.com>
Sun, 27 Feb 2011 00:08:11 +0000 (02:08 +0200)
committerMarko Kreen <markokr@gmail.com>
Mon, 28 Feb 2011 05:52:14 +0000 (07:52 +0200)
Make sure <ctype.h> functions accept 'char' values.

On arches where plain 'char' is signed, <ctype.h> functions
need to work on negative values.

Some OSes do it already (Linux), some don't (BSD, Windows).

14 files changed:
doc/mainpage.dox
m4/usual.m4
test/Makefile
test/force_compat.sed
test/test_common.c
test/test_common.h
test/test_ctype.c [new file with mode: 0644]
usual/cfparser.c
usual/ctype.h [new file with mode: 0644]
usual/logging.c
usual/mdict.c
usual/pgutil.c
usual/regex.c
usual/string.c

index d22d72f89b1974c65053444fb911f6f729e058ce..0e61c4a0a643e5499fdf9cb87223a9e7d3cd3446 100644 (file)
@@ -28,6 +28,7 @@
  * <table>
  * <tr><th colspan=2>  Compat includes  </th></tr>
  * <tr><td>  <usual/base.h>          </td><td>  Base C environment  </td></tr>
+ * <tr><td>  <usual/ctype.h>         </td><td>  ctype compat  </td></tr>
  * <tr><td>  <usual/getopt.h>        </td><td>  Command line argument processing  </td></tr>
  * <tr><td>  <usual/err.h>           </td><td>  Error handling for command-line tools  </td></tr>
  * <tr><td>  <usual/netdb.h>         </td><td>  Async DNS lookup  </td></tr>
index 458e28a3a9350592d82bef13c2527d5c7ae8e158..3a50b4c31f7af2f2f8a06da759480235967b94fa 100644 (file)
@@ -130,7 +130,44 @@ AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
-])])
+])
+
+AC_MSG_CHECKING([whether <ctype.h> works on char values])
+_good_ctype=no
+AC_RUN_IFELSE([AC_LANG_SOURCE([
+#include <ctype.h>
+#define W(c) ((signed char)(c))
+int main(void)
+{
+       int c;
+
+       /* if char is unsigned, all is ok */
+       if ((int)(char)255 == (int)255)
+               return 0;
+
+       for (c = 128; c < 256; c++) {
+               if (isalpha(c) != isalpha(W(c))) return 1;
+               if (isalnum(c) != isalnum(W(c))) return 1;
+               if (isascii(c) != isascii(W(c))) return 1;
+               if (isblank(c) != isblank(W(c))) return 1;
+               if (iscntrl(c) != iscntrl(W(c))) return 1;
+               if (isdigit(c) != isdigit(W(c))) return 1;
+               if (islower(c) != islower(W(c))) return 1;
+               if (isprint(c) != isprint(W(c))) return 1;
+               if (ispunct(c) != ispunct(W(c))) return 1;
+               if (isspace(c) != isspace(W(c))) return 1;
+               if (isupper(c) != isupper(W(c))) return 1;
+               if (isxdigit(c) != isxdigit(W(c))) return 1;
+       }
+       return 0;
+}
+])], [_good_ctype=yes], [_good_ctype=no])
+AC_MSG_RESULT([$_good_ctype])
+if test $_good_ctype = yes; then
+  AC_DEFINE(HAVE_CTYPE_ON_CHAR, [1], [Define if <ctype.h> macros work on char.])
+fi
+
+])
 
 
 dnl
index 9fcf4932507bab016583c485f0aec7050905e37c..953d2529e1c22e9f3b24c5b171dccdff0557d3b4 100644 (file)
@@ -6,7 +6,7 @@ SRCS = test_string.c test_crypto.c test_aatree.c test_heap.c \
        test_cxalloc.c test_bits.c test_base.c test_netdb.c \
        test_cfparser.c test_endian.c test_hashtab.c test_mdict.c \
        test_shlist.c test_time.c test_hashing.c test_fileutil.c \
-       test_socket.c test_getopt.c
+       test_socket.c test_getopt.c test_ctype.c
 
 OBJS = $(addprefix obj/, $(SRCS:.c=.o))
 HDRS = test_common.h test_config.h tinytest.h tinytest_macros.h
index 828cd29bd8dbc51d101d3754594e1eb76fd46449..6ae33135adf06a5e780a8179be5f37c0d7d44fcd 100644 (file)
@@ -9,3 +9,4 @@
 /INET_NTOP/s,^,//,
 /INET_PTON/s,^,//,
 /GETOPT/s,^,//,
+/CTYPE_ON_CHAR/s,^,//,
index 6aae1f0496f2972b5bbf1f4674389ba8bb1cf61a..bfbbd94785c332d2cfed585c3646ce11db5397e1 100644 (file)
@@ -12,6 +12,7 @@ struct testgroup_t groups[] = {
        { "hashing/", hashing_tests },
        { "endian/", endian_tests },
        { "string/", string_tests },
+       { "ctype/", ctype_tests },
        { "heap/", heap_tests },
        { "hashtab/", hashtab_tests },
        { "list/", list_tests },
index 460cbc31a91870e8b8bb005703e48e960faf737c..2c5ff45885c95f01ee35d176cb4c81d6a87fd7cf 100644 (file)
@@ -32,4 +32,4 @@ extern struct testcase_t hashing_tests[];
 extern struct testcase_t fileutil_tests[];
 extern struct testcase_t socket_tests[];
 extern struct testcase_t getopt_tests[];
-
+extern struct testcase_t ctype_tests[];
diff --git a/test/test_ctype.c b/test/test_ctype.c
new file mode 100644 (file)
index 0000000..dcc80e8
--- /dev/null
@@ -0,0 +1,50 @@
+#include <usual/ctype.h>
+
+#include <string.h>
+#include "test_common.h"
+
+#include <stdio.h>
+
+/*
+ * if char works
+ */
+
+static void test_ctype_char(void *p)
+{
+       int c, cx;
+       for (c = 0; c < 256; c++) {
+               cx = (int)(char)c;
+               int_check(isalnum(c), isalnum(cx));
+               int_check(isalpha(c), isalpha(cx));
+               int_check(isascii(c), isascii(cx));
+               int_check(isblank(c), isblank(cx));
+               int_check(iscntrl(c), iscntrl(cx));
+               int_check(isdigit(c), isdigit(cx));
+               int_check(islower(c), islower(cx));
+               int_check(isgraph(c), isgraph(cx));
+               int_check(isprint(c), isprint(cx));
+               int_check(ispunct(c), ispunct(cx));
+               int_check(isspace(c), isspace(cx));
+               int_check(isupper(c), isupper(cx));
+               int_check(isxdigit(c), isxdigit(cx));
+               if (c == 255) {
+                       int_check(toupper(c), (unsigned char)toupper(cx));
+                       int_check(tolower(c), (unsigned char)tolower(cx));
+               } else {
+                       int_check(toupper(c), toupper(cx));
+                       int_check(tolower(c), tolower(cx));
+               }
+       }
+end:;
+}
+
+
+/*
+ * Describe
+ */
+
+struct testcase_t ctype_tests[] = {
+       { "ctype_char", test_ctype_char },
+       END_OF_TESTCASES
+};
+
index 1fd9a79114b730cf64cfd2cbe48359fffd694ebb..5b17fe3367e97a53a0d7ad4dd77207f95927f3b1 100644 (file)
 
 #include <usual/cfparser.h>
 
-#include <ctype.h>
 #include <string.h>
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
 
+#include <usual/ctype.h>
 #include <usual/fileutil.h>
 #include <usual/logging.h>
 #include <usual/time.h>
diff --git a/usual/ctype.h b/usual/ctype.h
new file mode 100644 (file)
index 0000000..6032199
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * ctype wrappers
+ *
+ * Copyright (c) 2011  Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * @file
+ * ctype compat.
+ *
+ * Provides wrappers that make sure the functions work on 'char' values.
+ *
+ * @note
+ * POSIX requires that these functions accept EOF/-1 in addition
+ * to ordinary byte values.  That means when working on 'char',
+ * the functions cannot differetiate between 0xFF and EOF.
+ * As no code should give EOF to <ctype.h> functions and no code
+ * should depend whether 0xFF is labeled ispunct() or not,
+ * it seems no worthwhile to fix it.
+ */
+
+#ifndef _USUAL_CTYPE_H_
+#define _USUAL_CTYPE_H_
+
+#include <usual/base.h>
+
+#include <ctype.h>
+
+#ifndef HAVE_CTYPE_ON_CHAR
+
+#define _WRAP_CTYPE_FN(name) \
+       static inline int safe_ ## name (int c) { \
+               return name((unsigned char)(c)); \
+       }
+
+_WRAP_CTYPE_FN(isalnum)
+#undef isalnum
+/** Safe isalnum */
+#define isalnum(c) safe_isalnum(c)
+
+_WRAP_CTYPE_FN(isalpha)
+#undef isalpha
+/** Safe isalpha */
+#define isalpha(c) safe_isalpha(c)
+
+_WRAP_CTYPE_FN(isascii)
+#undef isascii
+/** Safe isascii */
+#define isascii(c) safe_isascii(c)
+
+_WRAP_CTYPE_FN(isblank)
+#undef isblank
+/** Safe isblank */
+#define isblank(c) safe_isblank(c)
+
+_WRAP_CTYPE_FN(iscntrl)
+#undef iscntrl
+/** Safe iscntrl */
+#define iscntrl(c) safe_iscntrl(c)
+
+_WRAP_CTYPE_FN(isdigit)
+#undef isdigit
+/** Safe isdigit */
+#define isdigit(c) safe_isdigit(c)
+
+_WRAP_CTYPE_FN(isgraph)
+#undef isgraph
+/** Safe isgraph */
+#define isgraph(c) safe_isgraph(c)
+
+_WRAP_CTYPE_FN(islower)
+#undef islower
+/** Safe islower */
+#define islower(c) safe_islower(c)
+
+_WRAP_CTYPE_FN(isprint)
+#undef isprint
+/** Safe isprint */
+#define isprint(c) safe_isprint(c)
+
+_WRAP_CTYPE_FN(ispunct)
+#undef ispunct
+/** Safe ispunct */
+#define ispunct(c) safe_ispunct(c)
+
+_WRAP_CTYPE_FN(isspace)
+#undef isspace
+/** Safe isspace */
+#define isspace(c) safe_isspace(c)
+
+_WRAP_CTYPE_FN(isupper)
+#undef isupper
+/** Safe isupper */
+#define isupper(c) safe_isupper(c)
+
+_WRAP_CTYPE_FN(isxdigit)
+#undef isxdigit
+/** Safe isxdigit */
+#define isxdigit(c) safe_isxdigit(c)
+
+_WRAP_CTYPE_FN(tolower)
+#undef tolower
+/** Safe tolower */
+#define tolower(c) safe_tolower(c)
+
+_WRAP_CTYPE_FN(toupper)
+#undef toupper
+/** Safe toupper */
+#define toupper(c) safe_toupper(c)
+
+#undef _WRAP_CTYPE_FN
+
+#endif /* HAVE_BROKEN_CTYPE */
+
+#endif /* _USUAL_CTYPE_H_ */
+
index d9ae024dbb42e88aea875ebe42cb9e240edee586..54a4c17354cd2ff3b5d3b17b976cc3a2c29a147d 100644 (file)
@@ -20,8 +20,8 @@
 
 #include <stdio.h>
 #include <stdarg.h>
-#include <ctype.h>
 
+#include <usual/ctype.h>
 #include <usual/string.h>
 #include <usual/time.h>
 #include <usual/err.h>
index fcece184c98ed705d59a8fe3fe93546f3340ccdb..6f6857d4440e83e699ebce54d2f7b67321b9b57d 100644 (file)
@@ -7,8 +7,7 @@
 #include <usual/cbtree.h>
 #include <usual/mbuf.h>
 #include <usual/string.h>
-
-#include <ctype.h>
+#include <usual/ctype.h>
 
 struct MDict {
        struct CBTree *tree;
index 418cbe21a1d504a0cb35ee87fb9c7684cdcb8ceb..9a29b6ddaeeffaa088f7f0d248bf0288a3c3a44f 100644 (file)
@@ -7,7 +7,7 @@
 
 #include <usual/pgutil.h>
 
-#include <ctype.h>
+#include <usual/ctype.h>
 
 /* str -> E'str' */
 bool pg_quote_literal(char *_dst, const char *_src, int dstlen)
index 767f85d9fc814909740950978fab73fe9ffd5315..5196d0db4b6806ab5bbbfa3bf8d36c3a35189a0e 100644 (file)
@@ -42,7 +42,7 @@
 #ifndef USE_SYSTEM_REGEX
 
 #include <usual/mempool.h>
-#include <ctype.h>
+#include <usual/ctype.h>
 #include <string.h>
 #include <stdio.h>
 
index 26a21e205185dec8a1da81aeff62ea9eb9aa2f4a..3246b97655c09118c0951eee06776344ca65f56c 100644 (file)
@@ -20,9 +20,9 @@
 
 #include <usual/mbuf.h>
 #include <usual/statlist.h>
+#include <usual/ctype.h>
 
 #include <errno.h>
-#include <ctype.h>
 
 /*
  * Dynamic list of strings.