diff --git a/.gitignore b/.gitignore
index 57fd4c6d..5c0c2bac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ data/ucd
src/libucd.la
tests/printucddata
+tests/printucddata_cpp
# test output:
diff --git a/Makefile.am b/Makefile.am
index 74c11df6..787b967f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,6 +6,7 @@ mimedir = $(xdgdatadir)/mime
xmldir = $(mimedir)/packages
AM_LDFLAGS = ${LTLIBINTL}
+AM_CFLAGS = -Isrc/include -Isrc
AM_CXXFLAGS = -Isrc/include -Isrc
ACLOCAL_AMFLAGS = -I m4
@@ -113,19 +114,29 @@ src_libucd_la_SOURCES = \
############################# tests ###########################################
noinst_bin_PROGRAMS += tests/printucddata
-tests_printucddata_SOURCES = tests/printucddata.cpp
+tests_printucddata_SOURCES = tests/printucddata.c
tests_printucddata_LDADD = src/libucd.la
+noinst_bin_PROGRAMS += tests/printucddata_cpp
+tests_printucddata_cpp_SOURCES = tests/printucddata_cpp.cpp
+tests_printucddata_cpp_LDADD = src/libucd.la
+
tests/unicode-data.expected: tools/printdata.py tools/ucd.py \
data/ucd/UnicodeData.txt \
data/ucd/PropList.txt \
data/ucd/Scripts.txt
tools/printdata.py ${UCD_ROOTDIR} ${UCD_FLAGS} > $@
-tests/unicode-data.actual: tests/printucddata
+tests/unicode-data-capi.actual: tests/printucddata
tests/printucddata > $@
-tests/unicode-data.diff: tests/unicode-data.expected tests/unicode-data.actual
- diff -U0 tests/unicode-data.expected tests/unicode-data.actual > tests/unicode-data.diff
+tests/unicode-data-cppapi.actual: tests/printucddata_cpp
+ tests/printucddata_cpp > $@
-check: tests/unicode-data.diff
+tests/unicode-data-capi.diff: tests/unicode-data.expected tests/unicode-data-capi.actual
+ diff -U0 tests/unicode-data.expected tests/unicode-data-capi.actual > tests/unicode-data-capi.diff
+
+tests/unicode-data-cppapi.diff: tests/unicode-data.expected tests/unicode-data-cppapi.actual
+ diff -U0 tests/unicode-data.expected tests/unicode-data-cppapi.actual > tests/unicode-data-cppapi.diff
+
+check: tests/unicode-data-capi.diff tests/unicode-data-cppapi.diff
diff --git a/tests/printucddata.c b/tests/printucddata.c
new file mode 100644
index 00000000..09b5f03a
--- /dev/null
+++ b/tests/printucddata.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012-2016 Reece H. Dunn
+ *
+ * This file is part of ucd-tools.
+ *
+ * ucd-tools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ucd-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ucd-tools. If not, see .
+ */
+
+#include "ucd/ucd.h"
+
+#include
+#include
+
+void fput_utf8c(FILE *out, codepoint_t c)
+{
+ if (c < 0x80)
+ fputc((uint8_t)c, out);
+ else if (c < 0x800)
+ {
+ fputc(0xC0 | (c >> 6), out);
+ fputc(0x80 + (c & 0x3F), out);
+ }
+ else if (c < 0x10000)
+ {
+ fputc(0xE0 | (c >> 12), out);
+ fputc(0x80 + ((c >> 6) & 0x3F), out);
+ fputc(0x80 + (c & 0x3F), out);
+ }
+ else if (c < 0x200000)
+ {
+ fputc(0xF0 | (c >> 18), out);
+ fputc(0x80 + ((c >> 12) & 0x3F), out);
+ fputc(0x80 + ((c >> 6) & 0x3F), out);
+ fputc(0x80 + (c & 0x3F), out);
+ }
+}
+
+int fget_utf8c(FILE *in, codepoint_t *c)
+{
+ int ch = EOF;
+ if ((ch = fgetc(in)) == EOF) return 0;
+ if ((uint8_t)ch < 0x80)
+ *c = (uint8_t)ch;
+ else switch ((uint8_t)ch & 0xF0)
+ {
+ default:
+ *c = (uint8_t)ch & 0x1F;
+ if ((ch = fgetc(in)) == EOF) return 0;
+ *c = (*c << 6) + ((uint8_t)ch & 0x3F);
+ break;
+ case 0xE0:
+ *c = (uint8_t)ch & 0x0F;
+ if ((ch = fgetc(in)) == EOF) return 0;
+ *c = (*c << 6) + ((uint8_t)ch & 0x3F);
+ if ((ch = fgetc(in)) == EOF) return 0;
+ *c = (*c << 6) + ((uint8_t)ch & 0x3F);
+ break;
+ case 0xF0:
+ *c = (uint8_t)ch & 0x07;
+ if ((ch = fgetc(in)) == EOF) return 0;
+ *c = (*c << 6) + ((uint8_t)ch & 0x3F);
+ if ((ch = fgetc(in)) == EOF) return 0;
+ *c = (*c << 6) + ((uint8_t)ch & 0x3F);
+ if ((ch = fgetc(in)) == EOF) return 0;
+ *c = (*c << 6) + ((uint8_t)ch & 0x3F);
+ break;
+ }
+ return 1;
+}
+
+void uprintf_codepoint(FILE *out, codepoint_t c, char mode)
+{
+ switch (mode)
+ {
+ case 'c': // character
+ switch (c)
+ {
+ case '\t': fputs("\\t", out); break;
+ case '\r': fputs("\\r", out); break;
+ case '\n': fputs("\\n", out); break;
+ default: fput_utf8c(out, c); break;
+ }
+ break;
+ case 'h': // hexadecimal (lower)
+ fprintf(out, "%06x", c);
+ break;
+ case 'H': // hexadecimal (upper)
+ fprintf(out, "%06X", c);
+ break;
+ }
+}
+
+void uprintf(FILE *out, codepoint_t c, const char *format)
+{
+ while (*format) switch (*format)
+ {
+ case '%':
+ switch (*++format)
+ {
+ case 'c': // category
+ fputs(ucd_get_category_string(ucd_lookup_category(c)), out);
+ break;
+ case 'C': // category group
+ fputs(ucd_get_category_group_string(ucd_lookup_category_group(c)), out);
+ break;
+ case 'p': // codepoint
+ uprintf_codepoint(out, c, *++format);
+ break;
+ case 'L': // lowercase
+ uprintf_codepoint(out, ucd_tolower(c), *++format);
+ break;
+ case 's': // script
+ fputs(ucd_get_script_string(ucd_lookup_script(c)), out);
+ break;
+ case 'T': // titlecase
+ uprintf_codepoint(out, ucd_totitle(c), *++format);
+ break;
+ case 'U': // uppercase
+ uprintf_codepoint(out, ucd_toupper(c), *++format);
+ break;
+ case 'W': // whitespace
+ if (ucd_isspace(c))
+ fputs("White_Space", out);
+ break;
+ }
+ ++format;
+ break;
+ default:
+ fputc(*format, out);
+ ++format;
+ break;
+ }
+}
+
+void print_file(FILE *in)
+{
+ codepoint_t c = 0;
+ while (fget_utf8c(in, &c))
+ uprintf(stdout, c, "%pc\t%pH\t%s\t%c\t%Uc\t%Lc\t%Tc\t%W\n");
+}
+
+int main(int argc, char **argv)
+{
+ if (argc == 2)
+ {
+ if (!strcmp(argv[1], "--stdin") || !strcmp(argv[1], "-"))
+ print_file(stdin);
+ else
+ {
+ FILE *in = fopen(argv[1], "r");
+ if (in)
+ {
+ print_file(in);
+ fclose(in);
+ }
+ else
+ fprintf(stdout, "cannot open `%s`\n", argv[1]);
+ }
+ }
+ else
+ {
+ for (codepoint_t c = 0; c <= 0x10FFFF; ++c)
+ uprintf(stdout, c, "%pH %s %C %c %UH %LH %TH %W\n");
+ }
+ return 0;
+}
diff --git a/tests/printucddata.cpp b/tests/printucddata_cpp.cpp
similarity index 99%
rename from tests/printucddata.cpp
rename to tests/printucddata_cpp.cpp
index 7263e54d..60a2b04f 100644
--- a/tests/printucddata.cpp
+++ b/tests/printucddata_cpp.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2015 Reece H. Dunn
+ * Copyright (C) 2012-2016 Reece H. Dunn
*
* This file is part of ucd-tools.
*