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).
* <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>
#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
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
/INET_NTOP/s,^,//,
/INET_PTON/s,^,//,
/GETOPT/s,^,//,
+/CTYPE_ON_CHAR/s,^,//,
{ "hashing/", hashing_tests },
{ "endian/", endian_tests },
{ "string/", string_tests },
+ { "ctype/", ctype_tests },
{ "heap/", heap_tests },
{ "hashtab/", hashtab_tests },
{ "list/", list_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[];
--- /dev/null
+#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
+};
+
#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>
--- /dev/null
+/*
+ * 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_ */
+
#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>
#include <usual/cbtree.h>
#include <usual/mbuf.h>
#include <usual/string.h>
-
-#include <ctype.h>
+#include <usual/ctype.h>
struct MDict {
struct CBTree *tree;
#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)
#ifndef USE_SYSTEM_REGEX
#include <usual/mempool.h>
-#include <ctype.h>
+#include <usual/ctype.h>
#include <string.h>
#include <stdio.h>
#include <usual/mbuf.h>
#include <usual/statlist.h>
+#include <usual/ctype.h>
#include <errno.h>
-#include <ctype.h>
/*
* Dynamic list of strings.