[PATCH 0/6] Rewrite chewing internal in Rust #347

83 views
Skip to first unread message

Kan-Ru Chen

unread,
Jan 26, 2023, 8:42:27 PM1/26/23
to chewin...@googlegroups.com, czc...@czchen.org, jser...@gmail.com, Kan-Ru Chen
Hi, I plan to merge the Rust rewrite in to mainline and continue the
development with the following plan.

## Goal

Rewrite libchewing in Rust, to encourage more radical development and
contribution.

The rough plan is as following

1. Deprecate autotools support because Cmake works pretty well, there's no need
to maintain two build systems.
2. Integrate Rust build system with CMake so we can link the static library
produced by Rust with C code.
3. Rewrite all core concepts in Rust (**we are here**)
4. Rewrite the line editor interface in Rust
5. Build libchewing dynamic library directly from Rust (*optional*)
6. Publish stable release of the chewing crate <https://crates.io/crates/chewing>

Current state

* All zhuyin, dictionary, conversion algorithm are available in Rust
* init_database is rewrote in Rust using the new chewing crate
* About 30% of the new code is documented <https://docs.rs/chewing>

To do

* Design and rewrite the symbol table feature, maybe we can build on the new
dictionary format
* Design and write the line editor that should provide similar API provided by
chewingio.c
* choice.c is intertwined with chewingio.c and chewingutil.c so we probably
need to consider it in this design
* Document and stabilize the public API

## New features

* Public Bopomofo & Syllable API
* Public Dictionary reader and builder API
* New static dictionary format defined in RIFF container format
* Platform independent, extensively documented
* Allow embedding metadata including author, copyright, software used, etc.
* New sqlite user dictionary format and auto migration support, in order to
support the same dictionary interface.
* New stackable dictionary. Allow users to share and stack user defined
dictionary on top of system dictionary.

## Deprecated feature

* Plain text user dictionary (hash.dat)
* Old binary user dictionary (uhash.dat)
* Can always provide auto migrator if needed. Patches welcome.

Please review! The same PR is available at <https://github.com/chewing/libchewing/pull/347>

Kan-Ru Chen (6):
build: deprecate autotools
build: refactor CMakeLists.txt
build: introduce rust implemented public interfaces
build: introduce rust implemented internals
rust: full internal implementation
rust: publish to crates.io

.gitignore | 14 +-
.gitmodules | 3 +
CMakeLists.txt | 249 +-
Cargo.lock | 751 +
Cargo.toml | 33 +
Makefile.am | 72 -
autogen.sh | 7 -
capi/chewing-internal/Cargo.toml | 17 +
capi/chewing-internal/build.rs | 8 +
capi/chewing-internal/cbindgen.toml | 34 +
.../include/chewing_internal.h | 512 +
capi/chewing-internal/src/binding.rs | 13 +
capi/chewing-internal/src/bopomofo.rs | 242 +
capi/chewing-internal/src/compat.rs | 11 +
capi/chewing-internal/src/conversion.rs | 94 +
capi/chewing-internal/src/dict.rs | 165 +
capi/chewing-internal/src/ffi.rs | 21 +
capi/chewing-internal/src/key2pho.rs | 132 +
capi/chewing-internal/src/lib.rs | 11 +
capi/chewing-internal/src/path.rs | 86 +
capi/chewing-internal/src/types.rs | 256 +
capi/chewing-internal/src/userphrase.rs | 299 +
capi/chewing-internal/src/utf8.rs | 94 +
capi/chewing-public/Cargo.toml | 15 +
capi/chewing-public/build.rs | 8 +
capi/chewing-public/cbindgen.toml | 84 +
capi/chewing-public/include/chewing_rs.h | 134 +
capi/chewing-public/src/io.rs | 18 +
capi/chewing-public/src/lib.rs | 1 +
capi/chewing-public/src/types.rs | 47 +
cmake/corrosion | 1 +
configure.ac | 235 -
data/CMakeLists.txt | 74 +
data/Makefile.am | 33 -
data/word.src | 26096 ++++++++++++++++
doc/CMakeLists.txt | 21 +
doc/Makefile.am | 3 -
include/chewing.h | 6 +-
include/chewingio.h | 6 +-
include/global.h | 24 -
include/internal/bopomofo-private.h | 4 +
include/internal/chewing-private.h | 56 +-
include/internal/hash-private.h | 1 -
include/internal/key2pho-private.h | 13 +-
include/mod_aux.h | 6 +-
m4/ax_pthread.m4 | 317 -
m4/ax_with_curses.m4 | 518 -
src/Makefile.am | 62 -
src/bopomofo.c | 21 +
src/chewing-sql.c | 82 +-
src/chewing.c | 0
src/chewingio.c | 223 +-
src/chewingutil.c | 166 +-
src/choice.c | 43 +-
src/common/Makefile.am | 19 -
src/common/key2pho.c | 22 +-
src/conversion.rs | 38 +
src/conversion/chewing_conversion.rs | 698 +
src/dict.c | 20 +-
src/dictionary.rs | 445 +
src/dictionary/layered.rs | 188 +
src/dictionary/sqlite.rs | 625 +
src/dictionary/trie.rs | 1224 +
src/editor.rs | 6 +
src/editor/estimate.rs | 112 +
src/editor/keymap.rs | 159 +
src/editor/layout.rs | 87 +
src/editor/layout/dc26.rs | 185 +
src/editor/layout/et.rs | 135 +
src/editor/layout/et26.rs | 222 +
src/editor/layout/ginyieh.rs | 134 +
src/editor/layout/hsu.rs | 281 +
src/editor/layout/ibm.rs | 135 +
src/editor/layout/pinyin.rs | 498 +
src/editor/layout/standard.rs | 136 +
src/hash.c | 54 +-
src/lib.rs | 9 +
src/mod_aux.c | 9 +-
src/path.rs | 58 +
src/pinyin.c | 42 +-
src/porting_layer/Makefile.am | 9 -
src/porting_layer/src/Makefile.am | 17 -
src/private.h | 37 +
src/tools/Makefile.am | 28 -
src/tree.c | 27 +-
src/userphrase-hash.c | 8 +-
src/userphrase-sql.c | 76 +-
src/zhuyin.rs | 5 +
src/zhuyin/bopomofo.rs | 298 +
src/zhuyin/syllable.rs | 485 +
test/CMakeLists.txt | 105 +
test/Makefile.am | 94 -
test/test-bopomofo.c | 6 +-
test/test-key2pho.c | 1 -
test/test-path.c | 11 +
test/testhelper.c | 8 +-
test/testhelper.h | 34 +-
thirdparty/sqlite-amalgamation/Makefile.am | 23 -
tools/Cargo.toml | 12 +
tools/src/bin/init_database.rs | 121 +
100 files changed, 36387 insertions(+), 2001 deletions(-)
create mode 100644 .gitmodules
create mode 100644 Cargo.lock
create mode 100644 Cargo.toml
delete mode 100644 Makefile.am
delete mode 100755 autogen.sh
create mode 100644 capi/chewing-internal/Cargo.toml
create mode 100644 capi/chewing-internal/build.rs
create mode 100644 capi/chewing-internal/cbindgen.toml
create mode 100644 capi/chewing-internal/include/chewing_internal.h
create mode 100644 capi/chewing-internal/src/binding.rs
create mode 100644 capi/chewing-internal/src/bopomofo.rs
create mode 100644 capi/chewing-internal/src/compat.rs
create mode 100644 capi/chewing-internal/src/conversion.rs
create mode 100644 capi/chewing-internal/src/dict.rs
create mode 100644 capi/chewing-internal/src/ffi.rs
create mode 100644 capi/chewing-internal/src/key2pho.rs
create mode 100644 capi/chewing-internal/src/lib.rs
create mode 100644 capi/chewing-internal/src/path.rs
create mode 100644 capi/chewing-internal/src/types.rs
create mode 100644 capi/chewing-internal/src/userphrase.rs
create mode 100644 capi/chewing-internal/src/utf8.rs
create mode 100644 capi/chewing-public/Cargo.toml
create mode 100644 capi/chewing-public/build.rs
create mode 100644 capi/chewing-public/cbindgen.toml
create mode 100644 capi/chewing-public/include/chewing_rs.h
create mode 100644 capi/chewing-public/src/io.rs
create mode 100644 capi/chewing-public/src/lib.rs
create mode 100644 capi/chewing-public/src/types.rs
create mode 160000 cmake/corrosion
delete mode 100644 configure.ac
create mode 100644 data/CMakeLists.txt
delete mode 100644 data/Makefile.am
create mode 100644 data/word.src
create mode 100644 doc/CMakeLists.txt
delete mode 100644 doc/Makefile.am
delete mode 100644 m4/ax_pthread.m4
delete mode 100644 m4/ax_with_curses.m4
delete mode 100644 src/Makefile.am
create mode 100644 src/chewing.c
delete mode 100644 src/common/Makefile.am
create mode 100644 src/conversion.rs
create mode 100644 src/conversion/chewing_conversion.rs
create mode 100644 src/dictionary.rs
create mode 100644 src/dictionary/layered.rs
create mode 100644 src/dictionary/sqlite.rs
create mode 100644 src/dictionary/trie.rs
create mode 100644 src/editor.rs
create mode 100644 src/editor/estimate.rs
create mode 100644 src/editor/keymap.rs
create mode 100644 src/editor/layout.rs
create mode 100644 src/editor/layout/dc26.rs
create mode 100644 src/editor/layout/et.rs
create mode 100644 src/editor/layout/et26.rs
create mode 100644 src/editor/layout/ginyieh.rs
create mode 100644 src/editor/layout/hsu.rs
create mode 100644 src/editor/layout/ibm.rs
create mode 100644 src/editor/layout/pinyin.rs
create mode 100644 src/editor/layout/standard.rs
create mode 100644 src/lib.rs
create mode 100644 src/path.rs
delete mode 100644 src/porting_layer/Makefile.am
delete mode 100644 src/porting_layer/src/Makefile.am
delete mode 100644 src/tools/Makefile.am
create mode 100644 src/zhuyin.rs
create mode 100644 src/zhuyin/bopomofo.rs
create mode 100644 src/zhuyin/syllable.rs
create mode 100644 test/CMakeLists.txt
delete mode 100644 test/Makefile.am
delete mode 100644 thirdparty/sqlite-amalgamation/Makefile.am
create mode 100644 tools/Cargo.toml
create mode 100644 tools/src/bin/init_database.rs

--
2.39.1

Kan-Ru Chen

unread,
Jan 26, 2023, 8:42:39 PM1/26/23
to chewin...@googlegroups.com, czc...@czchen.org, jser...@gmail.com, Kan-Ru Chen
---
Makefile.am | 72 ---
autogen.sh | 7 -
configure.ac | 235 ----------
data/Makefile.am | 33 --
doc/Makefile.am | 3 -
m4/ax_pthread.m4 | 317 -------------
m4/ax_with_curses.m4 | 518 ---------------------
src/Makefile.am | 62 ---
src/common/Makefile.am | 19 -
src/porting_layer/Makefile.am | 9 -
src/porting_layer/src/Makefile.am | 17 -
src/tools/Makefile.am | 28 --
test/Makefile.am | 94 ----
thirdparty/sqlite-amalgamation/Makefile.am | 23 -
14 files changed, 1437 deletions(-)
delete mode 100644 Makefile.am
delete mode 100755 autogen.sh
delete mode 100644 configure.ac
delete mode 100644 data/Makefile.am
delete mode 100644 doc/Makefile.am
delete mode 100644 m4/ax_pthread.m4
delete mode 100644 m4/ax_with_curses.m4
delete mode 100644 src/Makefile.am
delete mode 100644 src/common/Makefile.am
delete mode 100644 src/porting_layer/Makefile.am
delete mode 100644 src/porting_layer/src/Makefile.am
delete mode 100644 src/tools/Makefile.am
delete mode 100644 test/Makefile.am
delete mode 100644 thirdparty/sqlite-amalgamation/Makefile.am

diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 1742a80..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,72 +0,0 @@
-ACLOCAL_AMFLAGS = -I m4
-AUTOMAKE_OPTIONS = foreign
-
-if WITH_INTERNAL_SQLITE3
-SUBDIRS = thirdparty/sqlite-amalgamation
-else
-SUBDIRS =
-endif
-SUBDIRS += src/tools data src test doc
-
-CMAKE_FILES = \
- cmake/config.h.in \
- cmake/FindCurses.cmake \
- cmake/version.texi.in \
- CMakeLists.txt \
- $(NULL)
-
-CONTRIB_FILES = \
- contrib/python/test.py \
- contrib/python/chewing.py \
- contrib/simple-select.c \
- contrib/Makefile \
- $(NULL)
-
-EXTRA_DIST = \
- autogen.sh \
- $(CMAKE_FILES) \
- $(CONTRIB_FILES) \
- $(NULL)
-
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = chewing.pc
-
-chewing_includedir = $(includedir)/chewing
-chewing_include_HEADERS = \
- include/chewingio.h \
- include/mod_aux.h \
- include/chewing.h \
- include/chewing-compat.h \
- include/global.h \
- $(NULL)
-
-noinst_HEADERS =\
- include/internal/chewing-private.h \
- include/internal/chewing-sql.h \
- include/internal/chewing-utf8-util.h \
- include/internal/chewingutil.h \
- include/internal/choice-private.h \
- include/internal/dict-private.h \
- include/internal/global-private.h \
- include/internal/hash-private.h \
- include/internal/key2pho-private.h \
- include/internal/memory-private.h \
- include/internal/pinyin-private.h \
- include/internal/tree-private.h \
- include/internal/userphrase-private.h \
- include/internal/bopomofo-private.h \
- thirdparty/sqlite-amalgamation/sqlite3ext.h \
- thirdparty/sqlite-amalgamation/sqlite3.h \
- $(NULL)
-
-dist_noinst_DATA = \
- README.md \
- $(NULL)
-
-# Maintainer's release target
-release:
- $(MAKE) distdir
- $(top_srcdir)/scripts/git2cl > $(distdir)/ChangeLog
- cat $(top_srcdir)/scripts/ChangeLog-svn >> $(distdir)/ChangeLog
- tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
- $(am__remove_distdir)
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755
index 8f66be3..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /bin/sh
-
-AUTORECONF_ARGS=-i
-mkdir -p m4
-AUTORECONF_ARGS="$AUTORECONF_ARGS -I m4"
-
-autoreconf $AUTORECONF_ARGS
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index e1c7399..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,235 +0,0 @@
-# Process this file with autoconf to produce a configure script.
-
-AC_PREREQ([2.65])
-
-# Update CMakeLists.txt if version is changed.
-AC_INIT([libchewing],[0.5.1],[chewin...@googlegroups.com])
-AC_CONFIG_AUX_DIR([build-aux])
-AC_CONFIG_SRCDIR([src/chewingio.c])
-AC_CONFIG_MACRO_DIR([m4])
-
-AC_SUBST(PACKAGE_VERSION)
-
-# libtool versioning for libchewing
-
-# Update CMakeLists.txt if one of MAJOR/MINOR/REVISION is updated.
-#
-# We use major/minor/revision instead of current/revision/age format here is due
-# to buggy behavior in libtool in BSD family. See the following link for detail
-# information.
-#
-# https://lists.gnu.org/archive/html/bug-libtool/2011-05/msg00007.html
-#
-# The following link shows versioning policies in FreeBSD for your reference.
-#
-# http://www.freebsd.org/doc/en/books/developers-handbook/policies-shlib.html
-
-# increment if the interface has incompatible update (changes, removals).
-LIBCHEWING_MAJOR=3
-
-# increment if the inteface has compatible update (additions).
-# Set to 0 if MAJOR is increased.
-LIBCHEWING_MINOR=3
-
-# increment any time the source changes
-# Set to 0 if MAJOR or MINOR is increased.
-LIBCHEWING_REVISION=1
-
-AC_SUBST(LIBCHEWING_MAJOR)
-AC_SUBST(LIBCHEWING_MINOR)
-AC_SUBST(LIBCHEWING_REVISION)
-
-# Define a string for the earliest version that this release has
-# binary compatibility with. This is used for module locations.
-#
-# Update CMakeFiles.txt if LIBCHEWING_BINARY_VERSION is changed.
-LIBCHEWING_BINARY_VERSION=1.0.0
-AC_SUBST(LIBCHEWING_BINARY_VERSION)
-
-AC_DEFINE_UNQUOTED(LIBCHEWING_BINARY_VERSION,
- "$LIBCHEWING_BINARY_VERSION", [The binary version of libchewing.])
-AC_DEFINE_UNQUOTED(LIBCHEWING_VERSION,
- "$PACKAGE_VERSION", [The release version of libchewing.])
-
-# Init automake stuff
-AM_INIT_AUTOMAKE
-AM_SILENT_RULES([yes])
-AC_CONFIG_HEADERS([include/config.h])
-
-# Init libtool
-LT_INIT([win32-dll pic-only])
-
-# libtool option to control which symbols are exported
-# right now, symbols starting with _ are not exported
-AC_SUBST(LIBTOOL_EXPORT_OPTIONS, ['-export-symbols-regex "^[[^_]].*"'])
-
-# Checks for programs.
-AC_PROG_CC
-AC_PROG_CC_C99
-AM_PROG_CC_C_O
-AC_LANG(C)
-AC_C_BIGENDIAN
-
-# Checks for library functions.
-AC_FUNC_MALLOC
-AC_CHECK_FUNCS([strtok_r asprintf])
-
-# options
-AC_ARG_WITH([sqlite3],
- AS_HELP_STRING([--with-sqlite3], [Use sqlite3 to store userphrase @<:@default=yes@:>@]),
- [],
- [with_sqlite3=yes])
-AM_CONDITIONAL([WITH_SQLITE3], [test x"$with_sqlite3" = x"yes"])
-
-AC_ARG_WITH([internal-sqlite3],
- AS_HELP_STRING([--with-internal-sqlite3], [Use internal sqlite3 instead of system-wide @<:@default=no@:>@]),
- [],
- [with_internal_sqlite3=no])
-AM_CONDITIONAL([WITH_INTERNAL_SQLITE3], [test x"$with_internal_sqlite3" = x"yes"])
-
-# for sqlite
-AS_IF([test x"$with_sqlite3" = x"yes"], [
- AC_DEFINE([WITH_SQLITE3], [1], [Use sqlite3 to store userphrase])
- AS_IF([test x"$with_internal_sqlite3" = x"no"],
- [
- AC_SEARCH_LIBS([sqlite3_open], [sqlite3],
- [AS_IF([test x$ac_cv_search_sqlite3_open != x"none required"],
- [AM_LDFLAGS="$AM_LDFLAGS $ac_cv_search_sqlite3_open"])],
- [AC_MSG_ERROR([unable to find the sqlite3() function])])
- ],
- [
- AC_SEARCH_LIBS([dlopen], [dl dld],
- [AS_IF([test x$ac_cv_search_dlopen != x"none required"],
- [AM_LDFLAGS="$AM_LDFLAGS $ac_cv_search_dlopen"])],
- [AC_MSG_ERROR([unable to find the dlopen() function])])
- AX_PTHREAD([
- AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
- AM_LDFLAGS="$AM_LDFLAGS $PTHREAD_LIBS"
- ], [AC_MSG_ERROR([cannot find pthread])])
- ])
-])
-
-# plat_mmap_posix
-AC_FUNC_MMAP
-
-# chewing-utf8-util.h
-AC_TYPE_SIZE_T
-
-# chewing-private.h
-AC_C_INLINE
-AC_TYPE_UINT16_T
-
-CC_FOR_BUILD=${CC_FOR_BUILD-${CC}}
-AC_SUBST(CC_FOR_BUILD)
-
-# Default CFLAGS
-AM_CFLAGS="$AM_CFLAGS -Wall $CFLAGS"
-
-# Add '_GNU_SOURCE' for asprintf
-AS_IF([test x$ac_cv_func_asprintf = xyes],
- [DEFAULT_CPPFLAGS="$DEFAULT_CPPFLAGS -D_GNU_SOURCE"])
-
-AX_WITH_CURSES
-AM_CONDITIONAL([ENABLE_TEXT_UI], [test x$ax_cv_ncursesw = "xyes"])
-
-# Options
-dnl Enable gcov for coverage test
-AC_ARG_ENABLE([gcov],
- [AS_HELP_STRING([--enable-gcov], [Turn on gcov support @<:@default=no@:>@])],
- [AS_CASE([${enableval}], [yes], [ENABLE_GCOV="true"], [ENABLE_GCOV="false"])],
- [ENABLE_GCOV="false"])
-AS_IF([test x$ENABLE_GCOV = x"true"], [AM_CFLAGS="$AM_CFLAGS --coverage" AM_LDFLAGS="$AM_LDFLAGS --coverage"])
-
-dnl Adds -fvisibility=hidden to CFLAGS if running with gcc 4 or greater.
-AC_MSG_CHECKING([whether the compiler supports GCC Visibility])
-dnl Check for gcc4 or greater
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-void
-#if defined(__GNUC__) && (__GNUC__ >= 4)
-foo () {};
-#endif
-]], [[]])],[
- has_visibility=yes
- AM_CFLAGS="$AM_CFLAGS -fvisibility=hidden"
-],[
- has_visibility=no
-])
-AC_MSG_RESULT($has_visibility)
-
-AC_CACHE_CHECK([whether the linker supports --no-undefined],
- [ac_cv_no_undefined],
- [
- saved_LDFLAGS="${LDFLAGS}"
- LDFLAGS="${LDFLAGS} -Wl,--no-undefined"
- AC_TRY_LINK([], [], [ac_cv_no_undefined=yes], [ac_cv_no_undefined=no])
- LDFLAGS="${saved_LDFLAGS}"
-])
-AS_IF([test x$ac_cv_no_undefined = "xyes"], [NO_UNDEFINED_LDFLAGS="-Wl,--no-undefined"])
-AC_SUBST(NO_UNDEFINED_LDFLAGS)
-
-# Platform-dependent
-dnl What kind of system are we using?
-case $host_os in
-win*|mingw*)
- SYSTEM=windows
- ;;
-cygwin*)
- SYSTEM=unix
- ;;
-*)
- SYSTEM=unix
- ;;
-esac
-
-case $SYSTEM in
-win)
-AC_DEFINE(UNDER_WINDOWS, 1,
- [Runtime is under Win32 environment])
- ;;
-unix)
-AC_DEFINE(UNDER_POSIX, 1,
- [Runtime is under POSIX environment])
- ;;
-esac
-
-AC_SUBST(AM_CFLAGS)
-AC_SUBST(AM_LDFLAGS)
-AC_SUBST(DEFAULT_CPPFLAGS)
-
-AC_CONFIG_LINKS([
- data/pinyin.tab:data/pinyin.tab
- data/swkb.dat:data/swkb.dat
- data/symbols.dat:data/symbols.dat
- test/stresstest.py:test/stresstest.py
-])
-
-AC_CONFIG_FILES([
- Makefile
- chewing.pc
- data/Makefile
- doc/Makefile
- libchewing.spec
- src/Makefile
- src/common/Makefile
- src/porting_layer/Makefile
- src/porting_layer/src/Makefile
- src/tools/Makefile
- test/Makefile
- thirdparty/sqlite-amalgamation/Makefile
-])
-AC_OUTPUT
-
-AC_MSG_RESULT([
-Build options:
- Build OS $build_os
- Host OS $host_os
- Version $PACKAGE_VERSION
- Install prefix $prefix
- Enable gcov $ENABLE_GCOV
- With sqlite3 $with_sqlite3
- With internal sqlite3 $with_internal_sqlite3
- Build TextUI sample $ax_cv_ncursesw
- Default CFLAGS $AM_CFLAGS
- Default CPPFLAGS $DEFAULT_CPPFLAGS
- Default LDFLAGS $AM_LDFLAGS
-])
diff --git a/data/Makefile.am b/data/Makefile.am
deleted file mode 100644
index 52b2791..0000000
--- a/data/Makefile.am
+++ /dev/null
@@ -1,33 +0,0 @@
-tooldir = $(top_builddir)/src/tools
-datas = \
- dictionary.dat \
- index_tree.dat \
- $(NULL)
-static_tables = pinyin.tab swkb.dat symbols.dat
-
-dist_noinst_DATA = \
- NOTE \
- phone.cin \
- phone.cin-CNS11643-complete.patch \
- svnrev \
- tsi.src \
- $(NULL)
-
-chewing_datadir = $(pkgdatadir)
-chewing_data_DATA = \
- $(static_tables) \
- $(datas) \
- $(NULL)
-
-all: $(datas)
-
-$(datas): gendata_stamp
-
-gendata_stamp: phone.cin tsi.src
- $(MAKE) gendata && \
- touch $@
-
-gendata:
- env LC_ALL=C $(tooldir)/init_database$(EXEEXT) $(top_srcdir)/data/phone.cin $(top_srcdir)/data/tsi.src
-
-CLEANFILES = $(datas) gendata_stamp
diff --git a/doc/Makefile.am b/doc/Makefile.am
deleted file mode 100644
index d784ea5..0000000
--- a/doc/Makefile.am
+++ /dev/null
@@ -1,3 +0,0 @@
-info_TEXINFOS = \
- libchewing.texi \
- $(NULL)
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
deleted file mode 100644
index 6d400ed..0000000
--- a/m4/ax_pthread.m4
+++ /dev/null
@@ -1,317 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
-#
-# DESCRIPTION
-#
-# This macro figures out how to build C programs using POSIX threads. It
-# sets the PTHREAD_LIBS output variable to the threads library and linker
-# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
-# flags that are needed. (The user can also force certain compiler
-# flags/libs to be tested by setting these environment variables.)
-#
-# Also sets PTHREAD_CC to any special C compiler that is needed for
-# multi-threaded programs (defaults to the value of CC otherwise). (This
-# is necessary on AIX to use the special cc_r compiler alias.)
-#
-# NOTE: You are assumed to not only compile your program with these flags,
-# but also link it with them as well. e.g. you should link with
-# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
-#
-# If you are only building threads programs, you may wish to use these
-# variables in your default LIBS, CFLAGS, and CC:
-#
-# LIBS="$PTHREAD_LIBS $LIBS"
-# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-# CC="$PTHREAD_CC"
-#
-# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
-# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
-# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
-#
-# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
-# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
-# PTHREAD_CFLAGS.
-#
-# ACTION-IF-FOUND is a list of shell commands to run if a threads library
-# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
-# is not found. If ACTION-IF-FOUND is not specified, the default action
-# will define HAVE_PTHREAD.
-#
-# Please let the authors know if this macro fails on any platform, or if
-# you have any other suggestions or comments. This macro was based on work
-# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
-# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
-# Alejandro Forero Cuervo to the autoconf macro repository. We are also
-# grateful for the helpful feedback of numerous users.
-#
-# Updated for Autoconf 2.68 by Daniel Richard G.
-#
-# LICENSE
-#
-# Copyright (c) 2008 Steven G. Johnson <ste...@alum.mit.edu>
-# Copyright (c) 2011 Daniel Richard G. <sk...@iSKUNK.ORG>
-#
-# This program 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.
-#
-# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception, the respective Autoconf Macro's copyright owner
-# gives unlimited permission to copy, distribute and modify the configure
-# scripts that are the output of Autoconf when processing the Macro. You
-# need not follow the terms of the GNU General Public License when using
-# or distributing such scripts, even though portions of the text of the
-# Macro appear in them. The GNU General Public License (GPL) does govern
-# all other use of the material that constitutes the Autoconf Macro.
-#
-# This special exception to the GPL applies to versions of the Autoconf
-# Macro released by the Autoconf Archive. When you make and distribute a
-# modified version of the Autoconf Macro, you may extend this special
-# exception to the GPL to apply to your modified version as well.
-
-#serial 20
-
-AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
-AC_DEFUN([AX_PTHREAD], [
-AC_REQUIRE([AC_CANONICAL_HOST])
-AC_LANG_PUSH([C])
-ax_pthread_ok=no
-
-# We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
-# It gets checked for in the link test anyway.
-
-# First of all, check if the user has set any of the PTHREAD_LIBS,
-# etcetera environment variables, and if threads linking works using
-# them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
- AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
- AC_MSG_RESULT($ax_pthread_ok)
- if test x"$ax_pthread_ok" = xno; then
- PTHREAD_LIBS=""
- PTHREAD_CFLAGS=""
- fi
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-fi
-
-# We must check for the threads library under a number of different
-# names; the ordering is very important because some systems
-# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
-# libraries is broken (non-POSIX).
-
-# Create a list of thread flags to try. Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
-
-ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
-
-# The ordering *is* (sometimes) important. Some notes on the
-# individual items follow:
-
-# pthreads: AIX (must check this before -lpthread)
-# none: in case threads are in libc; should be tried before -Kthread and
-# other compiler flags to prevent continual compiler warnings
-# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
-# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-# doesn't hurt to check since this sometimes defines pthreads too;
-# also defines -D_REENTRANT)
-# ... -mt is also the pthreads flag for HP/aCC
-# pthread: Linux, etcetera
-# --thread-safe: KAI C++
-# pthread-config: use pthread-config program (for GNU Pth library)
-
-case ${host_os} in
- solaris*)
-
- # On Solaris (at least, for some versions), libc contains stubbed
- # (non-functional) versions of the pthreads routines, so link-based
- # tests will erroneously succeed. (We need to link with -pthreads/-mt/
- # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
- # a function called by this macro, so we could check for that, but
- # who knows whether they'll stub that too in a future libc.) So,
- # we'll just look for -pthreads and -lpthread first:
-
- ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
- ;;
-
- darwin*)
- ax_pthread_flags="-pthread $ax_pthread_flags"
- ;;
-esac
-
-if test x"$ax_pthread_ok" = xno; then
-for flag in $ax_pthread_flags; do
-
- case $flag in
- none)
- AC_MSG_CHECKING([whether pthreads work without any flags])
- ;;
-
- -*)
- AC_MSG_CHECKING([whether pthreads work with $flag])
- PTHREAD_CFLAGS="$flag"
- ;;
-
- pthread-config)
- AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
- if test x"$ax_pthread_config" = xno; then continue; fi
- PTHREAD_CFLAGS="`pthread-config --cflags`"
- PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
- ;;
-
- *)
- AC_MSG_CHECKING([for the pthreads library -l$flag])
- PTHREAD_LIBS="-l$flag"
- ;;
- esac
-
- save_LIBS="$LIBS"
- save_CFLAGS="$CFLAGS"
- LIBS="$PTHREAD_LIBS $LIBS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
- # Check for various functions. We must include pthread.h,
- # since some functions may be macros. (On the Sequent, we
- # need a special flag -Kthread to make this header compile.)
- # We check for pthread_join because it is in -lpthread on IRIX
- # while pthread_create is in libc. We check for pthread_attr_init
- # due to DEC craziness with -lpthreads. We check for
- # pthread_cleanup_push because it is one of the few pthread
- # functions on Solaris that doesn't have a non-functional libc stub.
- # We try pthread_create on general principles.
- AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
- static void routine(void *a) { a = 0; }
- static void *start_routine(void *a) { return a; }],
- [pthread_t th; pthread_attr_t attr;
- pthread_create(&th, 0, start_routine, 0);
- pthread_join(th, 0);
- pthread_attr_init(&attr);
- pthread_cleanup_push(routine, 0);
- pthread_cleanup_pop(0) /* ; */])],
- [ax_pthread_ok=yes],
- [])
-
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-
- AC_MSG_RESULT($ax_pthread_ok)
- if test "x$ax_pthread_ok" = xyes; then
- break;
- fi
-
- PTHREAD_LIBS=""
- PTHREAD_CFLAGS=""
-done
-fi
-
-# Various other checks:
-if test "x$ax_pthread_ok" = xyes; then
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
- # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
- AC_MSG_CHECKING([for joinable pthread attribute])
- attr_name=unknown
- for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
- AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
- [int attr = $attr; return attr /* ; */])],
- [attr_name=$attr; break],
- [])
- done
- AC_MSG_RESULT($attr_name)
- if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
- AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
- [Define to necessary symbol if this constant
- uses a non-standard name on your system.])
- fi
-
- AC_MSG_CHECKING([if more special flags are required for pthreads])
- flag=no
- case ${host_os} in
- aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
- osf* | hpux*) flag="-D_REENTRANT";;
- solaris*)
- if test "$GCC" = "yes"; then
- flag="-D_REENTRANT"
- else
- flag="-mt -D_REENTRANT"
- fi
- ;;
- esac
- AC_MSG_RESULT(${flag})
- if test "x$flag" != xno; then
- PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
- fi
-
- AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
- ax_cv_PTHREAD_PRIO_INHERIT, [
- AC_LINK_IFELSE([
- AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
- [ax_cv_PTHREAD_PRIO_INHERIT=yes],
- [ax_cv_PTHREAD_PRIO_INHERIT=no])
- ])
- AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
- AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
-
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-
- # More AIX lossage: compile with *_r variant
- if test "x$GCC" != xyes; then
- case $host_os in
- aix*)
- AS_CASE(["x/$CC"],
- [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
- [#handle absolute path differently from PATH based program lookup
- AS_CASE(["x$CC"],
- [x/*],
- [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
- [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
- ;;
- esac
- fi
-fi
-
-test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
-
-AC_SUBST(PTHREAD_LIBS)
-AC_SUBST(PTHREAD_CFLAGS)
-AC_SUBST(PTHREAD_CC)
-
-# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$ax_pthread_ok" = xyes; then
- ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
- :
-else
- ax_pthread_ok=no
- $2
-fi
-AC_LANG_POP
-])dnl AX_PTHREAD
diff --git a/m4/ax_with_curses.m4 b/m4/ax_with_curses.m4
deleted file mode 100644
index 33a37ac..0000000
--- a/m4/ax_with_curses.m4
+++ /dev/null
@@ -1,518 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_with_curses.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_WITH_CURSES
-#
-# DESCRIPTION
-#
-# This macro checks whether a SysV or X/Open-compatible Curses library is
-# present, along with the associated header file. The NcursesW
-# (wide-character) library is searched for first, followed by Ncurses,
-# then the system-default plain Curses. The first library found is the
-# one returned.
-#
-# The following options are understood: --with-ncursesw, --with-ncurses,
-# --without-ncursesw, --without-ncurses. The "--with" options force the
-# macro to use that particular library, terminating with an error if not
-# found. The "--without" options simply skip the check for that library.
-# The effect on the search pattern is:
-#
-# (no options) - NcursesW, Ncurses, Curses
-# --with-ncurses --with-ncursesw - NcursesW only [*]
-# --without-ncurses --with-ncursesw - NcursesW only [*]
-# --with-ncursesw - NcursesW only [*]
-# --with-ncurses --without-ncursesw - Ncurses only [*]
-# --with-ncurses - NcursesW, Ncurses [**]
-# --without-ncurses --without-ncursesw - Curses only
-# --without-ncursesw - Ncurses, Curses
-# --without-ncurses - NcursesW, Curses
-#
-# [*] If the library is not found, abort the configure script.
-#
-# [**] If the second library (Ncurses) is not found, abort configure.
-#
-# The following preprocessor symbols may be defined by this macro if the
-# appropriate conditions are met:
-#
-# HAVE_CURSES - if any SysV or X/Open Curses library found
-# HAVE_CURSES_ENHANCED - if library supports X/Open Enhanced functions
-# HAVE_CURSES_COLOR - if library supports color (enhanced functions)
-# HAVE_CURSES_OBSOLETE - if library supports certain obsolete features
-# HAVE_NCURSESW - if NcursesW (wide char) library is to be used
-# HAVE_NCURSES - if the Ncurses library is to be used
-#
-# HAVE_CURSES_H - if <curses.h> is present and should be used
-# HAVE_NCURSESW_H - if <ncursesw.h> should be used
-# HAVE_NCURSES_H - if <ncurses.h> should be used
-# HAVE_NCURSESW_CURSES_H - if <ncursesw/curses.h> should be used
-# HAVE_NCURSES_CURSES_H - if <ncurses/curses.h> should be used
-#
-# (These preprocessor symbols are discussed later in this document.)
-#
-# The following output variable is defined by this macro; it is precious
-# and may be overridden on the ./configure command line:
-#
-# CURSES_LIB - library to add to xxx_LDADD
-#
-# The library listed in CURSES_LIB is NOT added to LIBS by default. You
-# need to add CURSES_LIB to the appropriate xxx_LDADD line in your
-# Makefile.am. For example:
-#
-# prog_LDADD = @CURSES_LIB@
-#
-# If CURSES_LIB is set on the configure command line (such as by running
-# "./configure CURSES_LIB=-lmycurses"), then the only header searched for
-# is <curses.h>. The user may use the CPPFLAGS precious variable to
-# override the standard #include search path. If the user needs to
-# specify an alternative path for a library (such as for a non-standard
-# NcurseW), the user should use the LDFLAGS variable.
-#
-# The following shell variables may be defined by this macro:
-#
-# ax_cv_curses - set to "yes" if any Curses library found
-# ax_cv_curses_enhanced - set to "yes" if Enhanced functions present
-# ax_cv_curses_color - set to "yes" if color functions present
-# ax_cv_curses_obsolete - set to "yes" if obsolete features present
-#
-# ax_cv_ncursesw - set to "yes" if NcursesW library found
-# ax_cv_ncurses - set to "yes" if Ncurses library found
-# ax_cv_plaincurses - set to "yes" if plain Curses library found
-# ax_cv_curses_which - set to "ncursesw", "ncurses", "plaincurses" or "no"
-#
-# These variables can be used in your configure.ac to determine the level
-# of support you need from the Curses library. For example, if you must
-# have either Ncurses or NcursesW, you could include:
-#
-# AX_WITH_CURSES
-# if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
-# AX_MSG_ERROR([requires either NcursesW or Ncurses library])
-# fi
-#
-# If any Curses library will do (but one must be present and must support
-# color), you could use:
-#
-# AX_WITH_CURSES
-# if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then
-# AC_MSG_ERROR([requires an X/Open-compatible Curses library with color])
-# fi
-#
-# Certain preprocessor symbols and shell variables defined by this macro
-# can be used to determine various features of the Curses library. In
-# particular, HAVE_CURSES and ax_cv_curses are defined if the Curses
-# library found conforms to the traditional SysV and/or X/Open Base Curses
-# definition. Any working Curses library conforms to this level.
-#
-# HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the
-# library supports the X/Open Enhanced Curses definition. In particular,
-# the wide-character types attr_t, cchar_t and wint_t, the functions
-# wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES
-# are checked. The Ncurses library does NOT conform to this definition,
-# although NcursesW does.
-#
-# HAVE_CURSES_COLOR and ax_cv_curses_color are defined if the library
-# supports color functions and macros such as COLOR_PAIR, A_COLOR,
-# COLOR_WHITE, COLOR_RED and init_pair(). These are NOT part of the
-# X/Open Base Curses definition, but are part of the Enhanced set of
-# functions. The Ncurses library DOES support these functions, as does
-# NcursesW.
-#
-# HAVE_CURSES_OBSOLETE and ax_cv_curses_obsolete are defined if the
-# library supports certain features present in SysV and BSD Curses but not
-# defined in the X/Open definition. In particular, the functions
-# getattrs(), getcurx() and getmaxx() are checked.
-#
-# To use the HAVE_xxx_H preprocessor symbols, insert the following into
-# your system.h (or equivalent) header file:
-#
-# #if defined HAVE_NCURSESW_CURSES_H
-# # include <ncursesw/curses.h>
-# #elif defined HAVE_NCURSESW_H
-# # include <ncursesw.h>
-# #elif defined HAVE_NCURSES_CURSES_H
-# # include <ncurses/curses.h>
-# #elif defined HAVE_NCURSES_H
-# # include <ncurses.h>
-# #elif defined HAVE_CURSES_H
-# # include <curses.h>
-# #else
-# # error "SysV or X/Open-compatible Curses header file required"
-# #endif
-#
-# For previous users of this macro: you should not need to change anything
-# in your configure.ac or Makefile.am, as the previous (serial 10)
-# semantics are still valid. However, you should update your system.h (or
-# equivalent) header file to the fragment shown above. You are encouraged
-# also to make use of the extended functionality provided by this version
-# of AX_WITH_CURSES, as well as in the additional macros
-# AX_WITH_CURSES_PANEL, AX_WITH_CURSES_MENU and AX_WITH_CURSES_FORM.
-#
-# LICENSE
-#
-# Copyright (c) 2009 Mark Pulford <ma...@kyne.com.au>
-# Copyright (c) 2009 Damian Pietras <da...@daper.net>
-# Copyright (c) 2012 Reuben Thomas <r...@sc3d.org>
-# Copyright (c) 2011 John Zaitseff <J.Zai...@zap.org.au>
-#
-# This program 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.
-#
-# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception, the respective Autoconf Macro's copyright owner
-# gives unlimited permission to copy, distribute and modify the configure
-# scripts that are the output of Autoconf when processing the Macro. You
-# need not follow the terms of the GNU General Public License when using
-# or distributing such scripts, even though portions of the text of the
-# Macro appear in them. The GNU General Public License (GPL) does govern
-# all other use of the material that constitutes the Autoconf Macro.
-#
-# This special exception to the GPL applies to versions of the Autoconf
-# Macro released by the Autoconf Archive. When you make and distribute a
-# modified version of the Autoconf Macro, you may extend this special
-# exception to the GPL to apply to your modified version as well.
-
-#serial 13
-
-AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES])
-AC_DEFUN([AX_WITH_CURSES], [
- AC_ARG_VAR([CURSES_LIB], [linker library for Curses, e.g. -lcurses])
- AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses],
- [force the use of Ncurses or NcursesW])],
- [], [with_ncurses=check])
- AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw],
- [do not use NcursesW (wide character support)])],
- [], [with_ncursesw=check])
-
- ax_saved_LIBS=$LIBS
- AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes],
- [ax_with_plaincurses=no], [ax_with_plaincurses=check])
-
- ax_cv_curses_which=no
-
- # Test for NcursesW
-
- AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncursesw" != xno], [
- LIBS="$ax_saved_LIBS -lncursesw"
-
- AC_CACHE_CHECK([for NcursesW wide-character library], [ax_cv_ncursesw], [
- AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
- [ax_cv_ncursesw=yes], [ax_cv_ncursesw=no])
- ])
- AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [
- AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library])
- ])
-
- AS_IF([test "x$ax_cv_ncursesw" = xyes], [
- ax_cv_curses=yes
- ax_cv_curses_which=ncursesw
- CURSES_LIB="-lncursesw"
- AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present])
- AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
-
- AC_CACHE_CHECK([for working ncursesw/curses.h], [ax_cv_header_ncursesw_curses_h], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@define _XOPEN_SOURCE_EXTENDED 1
- @%:@include <ncursesw/curses.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- chtype c = COLOR_PAIR(1) & A_COLOR;
- attr_t d = WA_NORMAL;
- cchar_t e;
- wint_t f;
- int g = getattrs(stdscr);
- int h = getcurx(stdscr) + getmaxx(stdscr);
- initscr();
- init_pair(1, COLOR_WHITE, COLOR_RED);
- wattr_set(stdscr, d, 0, NULL);
- wget_wch(stdscr, &f);
- ]])],
- [ax_cv_header_ncursesw_curses_h=yes],
- [ax_cv_header_ncursesw_curses_h=no])
- ])
- AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xyes], [
- ax_cv_curses_enhanced=yes
- ax_cv_curses_color=yes
- ax_cv_curses_obsolete=yes
- AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
- AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
- AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
- AC_DEFINE([HAVE_NCURSESW_CURSES_H], [1], [Define to 1 if <ncursesw/curses.h> is present])
- ])
-
- AC_CACHE_CHECK([for working ncursesw.h], [ax_cv_header_ncursesw_h], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@define _XOPEN_SOURCE_EXTENDED 1
- @%:@include <ncursesw.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- chtype c = COLOR_PAIR(1) & A_COLOR;
- attr_t d = WA_NORMAL;
- cchar_t e;
- wint_t f;
- int g = getattrs(stdscr);
- int h = getcurx(stdscr) + getmaxx(stdscr);
- initscr();
- init_pair(1, COLOR_WHITE, COLOR_RED);
- wattr_set(stdscr, d, 0, NULL);
- wget_wch(stdscr, &f);
- ]])],
- [ax_cv_header_ncursesw_h=yes],
- [ax_cv_header_ncursesw_h=no])
- ])
- AS_IF([test "x$ax_cv_header_ncursesw_h" = xyes], [
- ax_cv_curses_enhanced=yes
- ax_cv_curses_color=yes
- ax_cv_curses_obsolete=yes
- AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
- AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
- AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
- AC_DEFINE([HAVE_NCURSESW_H], [1], [Define to 1 if <ncursesw.h> is present])
- ])
-
- AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h_with_ncursesw], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@define _XOPEN_SOURCE_EXTENDED 1
- @%:@include <ncurses.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- chtype c = COLOR_PAIR(1) & A_COLOR;
- attr_t d = WA_NORMAL;
- cchar_t e;
- wint_t f;
- int g = getattrs(stdscr);
- int h = getcurx(stdscr) + getmaxx(stdscr);
- initscr();
- init_pair(1, COLOR_WHITE, COLOR_RED);
- wattr_set(stdscr, d, 0, NULL);
- wget_wch(stdscr, &f);
- ]])],
- [ax_cv_header_ncurses_h_with_ncursesw=yes],
- [ax_cv_header_ncurses_h_with_ncursesw=no])
- ])
- AS_IF([test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes], [
- ax_cv_curses_enhanced=yes
- ax_cv_curses_color=yes
- ax_cv_curses_obsolete=yes
- AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
- AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
- AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
- AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if <ncurses.h> is present])
- ])
-
- AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [
- AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h])
- ])
- ])
- ])
-
- # Test for Ncurses
-
- AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [
- LIBS="$ax_saved_LIBS -lncurses"
-
- AC_CACHE_CHECK([for Ncurses library], [ax_cv_ncurses], [
- AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
- [ax_cv_ncurses=yes], [ax_cv_ncurses=no])
- ])
- AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [
- AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library])
- ])
-
- AS_IF([test "x$ax_cv_ncurses" = xyes], [
- ax_cv_curses=yes
- ax_cv_curses_which=ncurses
- CURSES_LIB="-lncurses"
- AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present])
- AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
-
- AC_CACHE_CHECK([for working ncurses/curses.h], [ax_cv_header_ncurses_curses_h], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@include <ncurses/curses.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- chtype c = COLOR_PAIR(1) & A_COLOR;
- int g = getattrs(stdscr);
- int h = getcurx(stdscr) + getmaxx(stdscr);
- initscr();
- init_pair(1, COLOR_WHITE, COLOR_RED);
- ]])],
- [ax_cv_header_ncurses_curses_h=yes],
- [ax_cv_header_ncurses_curses_h=no])
- ])
- AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xyes], [
- ax_cv_curses_color=yes
- ax_cv_curses_obsolete=yes
- AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
- AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
- AC_DEFINE([HAVE_NCURSES_CURSES_H], [1], [Define to 1 if <ncurses/curses.h> is present])
- ])
-
- AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@include <ncurses.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- chtype c = COLOR_PAIR(1) & A_COLOR;
- int g = getattrs(stdscr);
- int h = getcurx(stdscr) + getmaxx(stdscr);
- initscr();
- init_pair(1, COLOR_WHITE, COLOR_RED);
- ]])],
- [ax_cv_header_ncurses_h=yes],
- [ax_cv_header_ncurses_h=no])
- ])
- AS_IF([test "x$ax_cv_header_ncurses_h" = xyes], [
- ax_cv_curses_color=yes
- ax_cv_curses_obsolete=yes
- AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
- AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
- AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if <ncurses.h> is present])
- ])
-
- AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno], [
- AC_MSG_WARN([could not find a working ncurses/curses.h or ncurses.h])
- ])
- ])
- ])
-
- # Test for plain Curses (or if CURSES_LIB was set by user)
-
- AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [
- AS_IF([test "x$CURSES_LIB" != x], [
- LIBS="$ax_saved_LIBS $CURSES_LIB"
- ], [
- LIBS="$ax_saved_LIBS -lcurses"
- ])
-
- AC_CACHE_CHECK([for Curses library], [ax_cv_plaincurses], [
- AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
- [ax_cv_plaincurses=yes], [ax_cv_plaincurses=no])
- ])
-
- AS_IF([test "x$ax_cv_plaincurses" = xyes], [
- ax_cv_curses=yes
- ax_cv_curses_which=plaincurses
- AS_IF([test "x$CURSES_LIB" = x], [
- CURSES_LIB="-lcurses"
- ])
- AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
-
- # Check for base conformance (and header file)
-
- AC_CACHE_CHECK([for working curses.h], [ax_cv_header_curses_h], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@include <curses.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- initscr();
- ]])],
- [ax_cv_header_curses_h=yes],
- [ax_cv_header_curses_h=no])
- ])
- AS_IF([test "x$ax_cv_header_curses_h" = xyes], [
- AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if <curses.h> is present])
-
- # Check for X/Open Enhanced conformance
-
- AC_CACHE_CHECK([for X/Open Enhanced Curses conformance], [ax_cv_plaincurses_enhanced], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@define _XOPEN_SOURCE_EXTENDED 1
- @%:@include <curses.h>
- @%:@ifndef _XOPEN_CURSES
- @%:@error "this Curses library is not enhanced"
- "this Curses library is not enhanced"
- @%:@endif
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- chtype c = COLOR_PAIR(1) & A_COLOR;
- attr_t d = WA_NORMAL;
- cchar_t e;
- wint_t f;
- initscr();
- init_pair(1, COLOR_WHITE, COLOR_RED);
- wattr_set(stdscr, d, 0, NULL);
- wget_wch(stdscr, &f);
- ]])],
- [ax_cv_plaincurses_enhanced=yes],
- [ax_cv_plaincurses_enhanced=no])
- ])
- AS_IF([test "x$ax_cv_plaincurses_enhanced" = xyes], [
- ax_cv_curses_enhanced=yes
- ax_cv_curses_color=yes
- AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
- AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
- ])
-
- # Check for color functions
-
- AC_CACHE_CHECK([for Curses color functions], [ax_cv_plaincurses_color], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@define _XOPEN_SOURCE_EXTENDED 1
- @%:@include <curses.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- chtype c = COLOR_PAIR(1) & A_COLOR;
- initscr();
- init_pair(1, COLOR_WHITE, COLOR_RED);
- ]])],
- [ax_cv_plaincurses_color=yes],
- [ax_cv_plaincurses_color=no])
- ])
- AS_IF([test "x$ax_cv_plaincurses_color" = xyes], [
- ax_cv_curses_color=yes
- AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
- ])
-
- # Check for obsolete functions
-
- AC_CACHE_CHECK([for obsolete Curses functions], [ax_cv_plaincurses_obsolete], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@include <curses.h>
- ]], [[
- chtype a = A_BOLD;
- int b = KEY_LEFT;
- int g = getattrs(stdscr);
- int h = getcurx(stdscr) + getmaxx(stdscr);
- initscr();
- ]])],
- [ax_cv_plaincurses_obsolete=yes],
- [ax_cv_plaincurses_obsolete=no])
- ])
- AS_IF([test "x$ax_cv_plaincurses_obsolete" = xyes], [
- ax_cv_curses_obsolete=yes
- AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
- ])
- ])
-
- AS_IF([test "x$ax_cv_header_curses_h" = xno], [
- AC_MSG_WARN([could not find a working curses.h])
- ])
- ])
- ])
-
- AS_IF([test "x$ax_cv_curses" != xyes], [ax_cv_curses=no])
- AS_IF([test "x$ax_cv_curses_enhanced" != xyes], [ax_cv_curses_enhanced=no])
- AS_IF([test "x$ax_cv_curses_color" != xyes], [ax_cv_curses_color=no])
- AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no])
-
- LIBS=$ax_saved_LIBS
-])dnl
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 2992af5..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,62 +0,0 @@
-SUBDIRS = common porting_layer
-
-AM_CPPFLAGS = \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/internal \
- -I$(top_srcdir)/src/porting_layer/include \
- -DCHEWING_DATADIR=\"$(datadir)/libchewing\" \
- $(USERPHRASE_CPPFLAGS) \
- $(DEFAULT_CPPFLAGS) \
- $(NULL)
-
-noinst_HEADERS = \
- private.h \
- $(NULL)
-
-lib_LTLIBRARIES = libchewing.la
-libchewing_la_SOURCES = \
- compat.c \
- chewingio.c \
- chewingutil.c \
- choice.c \
- dict.c \
- tree.c \
- bopomofo.c \
- pinyin.c \
- mod_aux.c \
- userphrase.c \
- $(USERPHRASE_SOURCES) \
- $(NULL)
-
-libchewing_la_LIBADD = \
- $(top_builddir)/src/common/libcommon.la \
- $(top_builddir)/src/porting_layer/src/libporting_layer.la \
- $(NULL)
-
-libchewing_la_LDFLAGS = \
- -version-number $(LIBCHEWING_MAJOR):$(LIBCHEWING_MINOR):$(LIBCHEWING_REVISION) \
- -rpath $(libdir) \
- -no-undefined \
- $(NO_UNDEFINED_LDFLAGS) \
- -export-symbols-regex "^(chewing)_" \
- $(NULL)
-
-if WITH_SQLITE3
-libchewing_la_SOURCES += \
- chewing-sql.c \
- userphrase-sql.c \
- $(NULL)
-
-if WITH_INTERNAL_SQLITE3
-AM_CPPFLAGS += -I$(top_srcdir)/thirdparty/sqlite-amalgamation
-libchewing_la_LIBADD += \
- $(top_builddir)/thirdparty/sqlite-amalgamation/libsqlite3-internal.la \
- $(NULL)
-endif
-
-else
-libchewing_la_SOURCES += \
- hash.c \
- userphrase-hash.c \
- $(NULL)
-endif
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
deleted file mode 100644
index 9b502d5..0000000
--- a/src/common/Makefile.am
+++ /dev/null
@@ -1,19 +0,0 @@
-AM_CPPFLAGS = \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/internal \
- -I$(top_srcdir)/src/porting_layer/include \
- $(DEFAULT_CPPFLAGS) \
- $(NULL)
-
-noinst_LTLIBRARIES = libcommon.la
-
-libcommon_la_SOURCES = \
- key2pho.c \
- chewing-utf8-util.c \
- $(NULL)
-
-if WITH_INTERNAL_SQLITE3
-AM_CPPFLAGS += \
- -I$(top_srcdir)/thirdparty/sqlite-amalgamation \
- $(NULL)
-endif
diff --git a/src/porting_layer/Makefile.am b/src/porting_layer/Makefile.am
deleted file mode 100644
index 005ab67..0000000
--- a/src/porting_layer/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-SUBDIRS = src
-
-noinst_HEADERS = \
- include/plat_mmap.h \
- include/plat_types.h \
- include/plat_path.h \
- include/sys/plat_posix.h \
- include/sys/plat_windows.h \
- $(NULL)
diff --git a/src/porting_layer/src/Makefile.am b/src/porting_layer/src/Makefile.am
deleted file mode 100644
index cdcb606..0000000
--- a/src/porting_layer/src/Makefile.am
+++ /dev/null
@@ -1,17 +0,0 @@
-MAINTAINERCLEANFILES = Makefile.in
-
-AM_CPPFLAGS = \
- -DCHEWING_DATADIR=\"$(datadir)/libchewing\" \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/internal \
- -I$(top_srcdir)/src/porting_layer/include \
- $(DEFAULT_CPPFLAGS) \
- $(NULL)
-
-noinst_LTLIBRARIES = libporting_layer.la
-libporting_layer_la_SOURCES = \
- plat_mmap_posix.c \
- plat_mmap_windows.c \
- plat_path.c \
- rpl_malloc.c \
- $(NULL)
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
deleted file mode 100644
index 2104f43..0000000
--- a/src/tools/Makefile.am
+++ /dev/null
@@ -1,28 +0,0 @@
-AM_CPPFLAGS = \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/internal \
- -I$(top_srcdir)/src \
- -I$(top_srcdir)/src/porting_layer/include \
- -I$(top_srcdir)/thirdparty/sqlite-amalgamation \
- $(DEFAULT_CPPFLAGS) \
- $(NULL)
-
-CC = $(CC_FOR_BUILD)
-AM_CFLAGS = $(CFLAGS_FOR_BUILD)
-
-noinst_PROGRAMS = init_database dump_database
-
-init_database_SOURCES = \
- init_database.c \
- $(top_srcdir)/src/common/chewing-utf8-util.c \
- $(top_srcdir)/src/common/key2pho.c \
- $(NULL)
-
-dump_database_SOURCES = \
- dump_database.c \
- $(top_srcdir)/src/common/chewing-utf8-util.c \
- $(top_srcdir)/src/common/key2pho.c \
- $(top_srcdir)/src/porting_layer/src/plat_mmap_posix.c \
- $(top_srcdir)/src/porting_layer/src/plat_mmap_windows.c \
- $(top_srcdir)/src/porting_layer/src/rpl_malloc.c \
- $(NULL)
diff --git a/test/Makefile.am b/test/Makefile.am
deleted file mode 100644
index 81cd1e4..0000000
--- a/test/Makefile.am
+++ /dev/null
@@ -1,94 +0,0 @@
-valgrind-check: testchewing
- @echo "Please waiting for the valgrind results..."
- libtool --mode=execute valgrind \
- --leak-check=full \
- --show-reachable=yes \
- ./testchewing < $(srcdir)/data/default-test.txt
-
-noinst_LTLIBRARIES = libtesthelper.la
-
-dist_noinst_DATA = \
- data/default-test.txt \
- data/dictionary.dat \
- data/index_tree.dat \
- data/phone.cin \
- data/pinyin.tab \
- data/swkb.dat \
- data/symbols.dat \
- data/tsi.src \
- $(NULL)
-
-libtesthelper_la_SOURCES = \
- testhelper.c \
- $(NULL)
-
-libtesthelper_la_LIBADD = \
- $(top_builddir)/src/libchewing.la \
- $(NULL)
-
-TESTS = $(NATIVE_TESTS)
-NATIVE_TESTS = \
- test-bopomofo \
- test-config \
- test-easy-symbol \
- test-error-handling \
- test-fullshape \
- test-key2pho \
- test-keyboard \
- test-keyboardless \
- test-logger \
- test-mmap \
- test-path \
- test-reset \
- test-regression \
- test-symbol \
- test-special-symbol \
- test-struct-size \
- test-userphrase \
- test-utf8 \
- $(NULL)
-
-check_HEADERS = \
- testhelper.h \
- $(NULL)
-
-check_PROGRAMS = \
- performance \
- testchewing \
- simulate \
- randkeystroke \
- stress \
- $(TEXT_UI_BIN) \
- $(NATIVE_TESTS) \
- $(NULL)
-
-if ENABLE_TEXT_UI
-TEXT_UI_BIN=genkeystroke
-genkeystroke_SOURCES = genkeystroke.c
-genkeystroke_LDADD = $(LDADD) @CURSES_LIB@
-else
-TEXT_UI_BIN=
-endif
-
-AM_CPPFLAGS = \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/include/internal \
- -I$(top_srcdir)/src/porting_layer/include \
- -I$(top_srcdir)/test/ \
- -I$(top_srcdir)/thirdparty/sqlite-amalgamation \
- -DCHEWING_DATA_PREFIX="\"$(abs_top_builddir)/data\"" \
- -DTEST_HASH_DIR="\"$(PWD)\"" \
- -DTEST_DATA_DIR="\"$(abs_srcdir)/data\"" \
- $(DEFAULT_CPPFLAGS) \
- $(CHECK_CFLAGS) \
- $(NULL)
-
-LDADD = \
- $(top_builddir)/src/porting_layer/src/libporting_layer.la \
- $(top_builddir)/test/libtesthelper.la \
- $(top_builddir)/src/common/libcommon.la \
- $(NULL)
-
-AM_LDFLAGS = -static
-
-CLEANFILES = uhash.dat materials.txt-random chewing.sqlite3 test.sqlite3
diff --git a/thirdparty/sqlite-amalgamation/Makefile.am b/thirdparty/sqlite-amalgamation/Makefile.am
deleted file mode 100644
index 4ca17a4..0000000
--- a/thirdparty/sqlite-amalgamation/Makefile.am
+++ /dev/null
@@ -1,23 +0,0 @@
-AM_CPPFLAGS = \
- $(DEFAULT_CPPFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = -static
-
-if WITH_SQLITE3
-if WITH_INTERNAL_SQLITE3
-noinst_LTLIBRARIES = libsqlite3-internal.la
-
-libsqlite3_internal_la_SOURCES = \
- sqlite3.c \
- $(NULL)
-
-check_PROGRAMS = sqlite3
-
-sqlite3_SOURCES = \
- shell.c \
- $(NULL)
-
-sqlite3_LDADD = libsqlite3-internal.la
-endif
-endif
--
2.39.1

Kan-Ru Chen

unread,
Jan 26, 2023, 8:42:43 PM1/26/23
to chewin...@googlegroups.com, czc...@czchen.org, jser...@gmail.com, Kan-Ru Chen
---
.gitignore | 11 ++-
CMakeLists.txt | 209 +++++---------------------------------------
data/CMakeLists.txt | 49 +++++++++++
doc/CMakeLists.txt | 21 +++++
test/CMakeLists.txt | 95 ++++++++++++++++++++
5 files changed, 192 insertions(+), 193 deletions(-)
create mode 100644 data/CMakeLists.txt
create mode 100644 doc/CMakeLists.txt
create mode 100644 test/CMakeLists.txt

diff --git a/.gitignore b/.gitignore
index 464539f..e917c79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,12 +42,17 @@ include/config.h.in
include/stamp-h1

# cmake generated files
+/build
config.h
/libchewing.*
/libsqlite3_library.a
/libtesthelper.a
/libuserphrase.a
/Testing
+/CTestTestfile.cmake
+/CMakeCache.txt
+/CMakeFiles/
+/cmake_install.cmake

# test suite
test/genkeystroke
@@ -116,12 +121,6 @@ doc/libchewing.tps
doc/libchewing.vr
doc/libchewing.vrs

-# cmake
-/CTestTestfile.cmake
-/CMakeCache.txt
-/CMakeFiles/
-/cmake_install.cmake
-
# Visual Studio
*.pdb
*.vcxproj*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 07112b0..406089d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,16 +1,16 @@
-cmake_minimum_required(VERSION 2.8.8)
+cmake_minimum_required(VERSION 3.19.0)
project(libchewing)

-# Update configure.ac if LIBCHEWING_VERSION is changed.
set(LIBCHEWING_VERSION 0.5.1)
set(PACKAGE_VERSION ${LIBCHEWING_VERSION})
-# Update configure.ac if LIBCHEWING_BINARY_VERSION is changed.
set(LIBCHEWING_BINARY_VERSION 1.0.0)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

+enable_testing()
+
if(UNIX)
set(CMAKE_C_FLAGS "-g -O2 -Wall -fPIC ${CMAKE_C_FLAGS}")
- add_definitions(-DUNDER_POSIX -DPIC)
+ add_compile_definitions(UNDER_POSIX PIC)
endif()

include(CheckCCompilerFlag)
@@ -18,7 +18,7 @@ include(CheckCCompilerFlag)
if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR
${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
set(CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}")
- add_definitions(-D_GNU_SOURCE)
+ add_compile_definitions(_GNU_SOURCE)
option(ENABLE_GCOV "Coverage support" false)
if(ENABLE_GCOV)
set(CMAKE_C_FLAGS "-coverage ${CMAKE_C_FLAGS}")
@@ -44,12 +44,12 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC")
# encoding, thus it will complain about invalid character. Use
# /wd4819 can suppress this warning.
set(CMAKE_C_FLAGS "/wd4819 ${CMAKE_C_FLAGS}")
- add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE)
- add_definitions(/D__func__=__FUNCTION__)
+ add_compile_definitions(_CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
+ add_compile_definitions(__func__=__FUNCTION__)

# MSVC 2015 supports `snprintf`, so no need to redefine it
if(MSVC_VERSION LESS 1900)
- add_definitions(/Dsnprintf=_snprintf)
+ add_compile_definitions(snprintf=_snprintf)
endif()

option(BUILD_DLL "Build dynamic link library (*.dll) instead of static lib." false)
@@ -61,7 +61,7 @@ if(${FVISIBILITY_HIDDEN})
endif()

# automake compatibility
-add_definitions(-DHAVE_CONFIG_H=1)
+add_compile_definitions(HAVE_CONFIG_H=1)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})

option(WITH_SQLITE3 "Use sqlite3 to store userphrase" true)
@@ -70,7 +70,6 @@ if(MSVC)
set(WITH_INTERNAL_SQLITE3 true)
endif()

-# Use valgrind when testing
option(USE_VALGRIND "Use valgrind when testing" true)

# Feature probe
@@ -121,9 +120,6 @@ set(TEST_BIN_DIR ${PROJECT_BINARY_DIR}/test)

include(GNUInstallDirs)

-set(INFO_SRC ${PROJECT_SOURCE_DIR}/doc/libchewing.texi)
-set(INFO_BIN ${PROJECT_BINARY_DIR}/doc/libchewing.info)
-
configure_file(
${PROJECT_SOURCE_DIR}/cmake/config.h.in
${PROJECT_BINARY_DIR}/include/config.h
@@ -155,11 +151,6 @@ include_directories(
${PROJECT_SOURCE_DIR}/src/porting_layer/include
)

-set(ALL_DATA
- ${DATA_BIN_DIR}/dictionary.dat
- ${DATA_BIN_DIR}/index_tree.dat
-)
-
set(ALL_INC
${INC_DIR}/chewing.h
${INC_DIR}/chewing-compat.h
@@ -168,166 +159,20 @@ set(ALL_INC
${INC_DIR}/mod_aux.h
)

-# info page
-find_program(MAKEINFO makeinfo)
-if (MAKEINFO)
- add_custom_command(
- OUTPUT
- ${INFO_BIN}
- COMMAND ${MAKEINFO} ${INFO_SRC} -o ${INFO_BIN} -I ${PROJECT_BINARY_DIR}/doc
- DEPENDS
- ${INFO_SRC}
- )
- add_custom_target(INFO ALL DEPENDS ${INFO_BIN})
- add_dependencies(check INFO)
-
- find_program(INSTALL_INFO NAMES ginstall-info install-info)
- if (INSTALL_INFO)
- install(FILES ${INFO_BIN} DESTINATION ${CMAKE_INSTALL_INFODIR})
- install(CODE "execute_process(COMMAND ${INSTALL_INFO} --info-dir=${CMAKE_INSTALL_INFODIR} ${INFO_BIN})")
- endif()
-endif()
-
-
-# We need to copy static data to binary tree when using out of tree build.
-set(ALL_STATIC_DATA
- ${DATA_BIN_DIR}/pinyin.tab
- ${DATA_BIN_DIR}/swkb.dat
- ${DATA_BIN_DIR}/symbols.dat
-)
-
-add_custom_target(all_static_data
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_SRC_DIR}/pinyin.tab ${DATA_BIN_DIR}/pinyin.tab
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_SRC_DIR}/swkb.dat ${DATA_BIN_DIR}/swkb.dat
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_SRC_DIR}/symbols.dat ${DATA_BIN_DIR}/symbols.dat
-)
-
-set(ALL_STATIC_TEST stresstest.py)
-foreach(target ${ALL_STATIC_TEST})
- add_custom_target(${target} ALL
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${TEST_SRC_DIR}/${target} ${TEST_BIN_DIR}/${target}
- )
- add_dependencies(check ${target})
-endforeach()
-
-# tools
-set(ALL_TOOLS init_database dump_database)
-add_executable(init_database ${TOOLS_SRC_DIR}/init_database.c $<TARGET_OBJECTS:common>)
-add_executable(dump_database
- ${TOOLS_SRC_DIR}/dump_database.c
- ${SRC_DIR}/porting_layer/src/plat_mmap_posix.c
- ${SRC_DIR}/porting_layer/src/plat_mmap_windows.c
- ${SRC_DIR}/porting_layer/src/rpl_malloc.c
- $<TARGET_OBJECTS:common>
-)
-set_target_properties(${ALL_TOOLS} PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY ${TOOLS_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TOOLS_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${TOOLS_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TOOLS_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${TOOLS_BIN_DIR}
-)
-
-# tools command
-add_custom_command(
- OUTPUT
- ${ALL_DATA}
- COMMAND ${CMAKE_COMMAND} -E make_directory ${DATA_BIN_DIR}
- COMMAND ${CMAKE_COMMAND} -E chdir ${DATA_BIN_DIR} ${TOOLS_BIN_DIR}/init_database ${DATA_SRC_DIR}/phone.cin ${DATA_SRC_DIR}/tsi.src
- DEPENDS
- ${ALL_TOOLS}
- ${DATA_SRC_DIR}/phone.cin
- ${DATA_SRC_DIR}/tsi.src
-)
-
-# test
-set(ALL_TESTCASES
- test-bopomofo
- test-config
- test-easy-symbol
- test-error-handling
- test-fullshape
- test-key2pho
- test-keyboard
- test-keyboardless
- test-logger
- test-mmap
- test-path
- test-regression
- test-reset
- test-special-symbol
- test-struct-size
- test-symbol
- test-userphrase
- test-utf8
-)
-set(ALL_TESTTOOLS
- performance
- randkeystroke
- simulate
- stress
- testchewing
-)
-
-if(${CURSES_FOUND})
- set(ALL_TESTTOOLS ${ALL_TESTTOOLS} genkeystroke)
-endif()
-
-enable_testing()
-
-set(ALL_TESTS ${ALL_TESTCASES} ${ALL_TESTTOOLS})
-
-foreach(target ${ALL_TESTCASES})
- add_test(${target} ${TEST_BIN_DIR}/${target})
-endforeach()
-
-if(USE_VALGRIND)
- find_program(VALGRIND valgrind)
- if(VALGRIND)
- foreach(target ${ALL_TESTCASES})
- add_test("valgrind-${target}" ${VALGRIND} --error-exitcode=255 --leak-check=full ${TEST_BIN_DIR}/${target})
- endforeach()
- endif()
-endif()
-
-foreach(target ${ALL_TESTS})
- add_executable(${target} ${TEST_SRC_DIR}/${target}.c)
- add_dependencies(${target} data all_static_data)
- add_dependencies(check ${target})
-endforeach()
+add_subdirectory(doc)
+add_subdirectory(test)
+add_subdirectory(data)

-add_library(testhelper STATIC
- ${TEST_SRC_DIR}/testhelper.c
- $<TARGET_OBJECTS:chewing>
- $<TARGET_OBJECTS:common>
-)
-target_link_libraries(testhelper userphrase)
-set_target_properties(testhelper PROPERTIES
- COMPILE_DEFINITIONS
- "CHEWING_DATA_PREFIX=\"${DATA_BIN_DIR}\";TEST_HASH_DIR=\"${TEST_BIN_DIR}\";TEST_DATA_DIR=\"${TEST_SRC_DIR}/data\";TESTDATA=\"${TEST_SRC_DIR}/default-test.txt\""
-)
+# library
+add_library(common OBJECT
+ ${INC_DIR}/internal/chewing-utf8-util.h
+ ${INC_DIR}/internal/key2pho-private.h
+ ${INC_DIR}/internal/memory-private.h

-set_target_properties(${ALL_TESTS} PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY ${TEST_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TEST_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TEST_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TEST_BIN_DIR}
- RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${TEST_BIN_DIR}
- COMPILE_DEFINITIONS
- "CHEWING_DATA_PREFIX=\"${DATA_BIN_DIR}\";TEST_HASH_DIR=\"${TEST_BIN_DIR}\";TEST_DATA_DIR=\"${TEST_SRC_DIR}/data\""
+ ${SRC_DIR}/common/chewing-utf8-util.c
+ ${SRC_DIR}/common/key2pho.c
)
-foreach(target ${ALL_TESTS})
- target_link_libraries(${target} testhelper)
-endforeach()
-
-if (${CURSES_FOUND})
- target_link_libraries(genkeystroke ${CURSES_LIBRARIES})
-endif()

-# data
-add_custom_target(data ALL DEPENDS ${ALL_DATA})
-
-# library
add_library(chewing OBJECT
${ALL_INC}
${INC_DIR}/internal/chewing-private.h
@@ -361,8 +206,8 @@ add_library(chewing OBJECT
${SRC_DIR}/userphrase.c
${SRC_DIR}/bopomofo.c
)
-set_target_properties(chewing PROPERTIES
- COMPILE_DEFINITIONS "CHEWING_DATADIR=\"${CMAKE_INSTALL_FULL_DATADIR}/libchewing\""
+target_compile_definitions(chewing PRIVATE
+ CHEWING_DATADIR=\"${CMAKE_INSTALL_FULL_DATADIR}/libchewing\"
)

if (WITH_SQLITE3)
@@ -407,7 +252,7 @@ endif()

if (BUILD_DLL OR NOT MSVC)
if (MSVC)
- add_definitions(-DCHEWINGDLL_EXPORTS)
+ add_compile_definitions(CHEWINGDLL_EXPORTS)
endif()
add_library(chewing_shared SHARED
$<TARGET_OBJECTS:chewing>
@@ -441,16 +286,6 @@ set_target_properties(${LIBS} PROPERTIES
VERSION 3.3.1
)

-add_library(common OBJECT
- ${INC_DIR}/internal/chewing-utf8-util.h
- ${INC_DIR}/internal/key2pho-private.h
- ${INC_DIR}/internal/memory-private.h
-
- ${SRC_DIR}/common/chewing-utf8-util.c
- ${SRC_DIR}/common/key2pho.c
-)
-
-
# install
install(FILES ${ALL_DATA} DESTINATION ${CMAKE_INSTALL_DATADIR}/libchewing)
install(FILES ${ALL_STATIC_DATA} DESTINATION ${CMAKE_INSTALL_DATADIR}/libchewing)
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
new file mode 100644
index 0000000..4cb0068
--- /dev/null
+++ b/data/CMakeLists.txt
@@ -0,0 +1,49 @@
+set(ALL_DATA
+ ${DATA_BIN_DIR}/dictionary.dat
+ ${DATA_BIN_DIR}/index_tree.dat
+)
+
+add_custom_target(data ALL DEPENDS ${ALL_DATA})
+
+# We need to copy static data to binary tree when using out of tree build.
+set(ALL_STATIC_DATA
+ ${DATA_BIN_DIR}/pinyin.tab
+ ${DATA_BIN_DIR}/swkb.dat
+ ${DATA_BIN_DIR}/symbols.dat
+)
+
+add_custom_target(all_static_data
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_SRC_DIR}/pinyin.tab ${DATA_BIN_DIR}/pinyin.tab
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_SRC_DIR}/swkb.dat ${DATA_BIN_DIR}/swkb.dat
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_SRC_DIR}/symbols.dat ${DATA_BIN_DIR}/symbols.dat
+)
+
+# tools
+set(ALL_TOOLS init_database dump_database)
+add_executable(init_database ${TOOLS_SRC_DIR}/init_database.c $<TARGET_OBJECTS:common>)
+add_executable(dump_database
+ ${TOOLS_SRC_DIR}/dump_database.c
+ ${SRC_DIR}/porting_layer/src/plat_mmap_posix.c
+ ${SRC_DIR}/porting_layer/src/plat_mmap_windows.c
+ ${SRC_DIR}/porting_layer/src/rpl_malloc.c
+ $<TARGET_OBJECTS:common>
+)
+set_target_properties(${ALL_TOOLS} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${TOOLS_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TOOLS_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${TOOLS_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TOOLS_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${TOOLS_BIN_DIR}
+)
+
+# tools command
+add_custom_command(
+ OUTPUT
+ ${ALL_DATA}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${DATA_BIN_DIR}
+ COMMAND ${CMAKE_COMMAND} -E chdir ${DATA_BIN_DIR} ${TOOLS_BIN_DIR}/init_database ${DATA_SRC_DIR}/phone.cin ${DATA_SRC_DIR}/tsi.src
+ DEPENDS
+ ${ALL_TOOLS}
+ ${DATA_SRC_DIR}/phone.cin
+ ${DATA_SRC_DIR}/tsi.src
+)
\ No newline at end of file
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
new file mode 100644
index 0000000..ab66a56
--- /dev/null
+++ b/doc/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(INFO_SRC ${PROJECT_SOURCE_DIR}/doc/libchewing.texi)
+set(INFO_BIN ${PROJECT_BINARY_DIR}/doc/libchewing.info)
+
+find_program(MAKEINFO makeinfo)
+if (MAKEINFO)
+ add_custom_command(
+ OUTPUT
+ ${INFO_BIN}
+ COMMAND ${MAKEINFO} ${INFO_SRC} -o ${INFO_BIN} -I ${PROJECT_BINARY_DIR}/doc
+ DEPENDS
+ ${INFO_SRC}
+ )
+ add_custom_target(INFO ALL DEPENDS ${INFO_BIN})
+ add_dependencies(check INFO)
+
+ find_program(INSTALL_INFO NAMES ginstall-info install-info)
+ if (INSTALL_INFO)
+ install(FILES ${INFO_BIN} DESTINATION ${CMAKE_INSTALL_INFODIR})
+ install(CODE "execute_process(COMMAND ${INSTALL_INFO} --info-dir=${CMAKE_INSTALL_INFODIR} ${INFO_BIN})")
+ endif()
+endif()
\ No newline at end of file
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..627525b
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,95 @@
+set(ALL_TESTCASES
+ test-bopomofo
+ test-config
+ test-easy-symbol
+ test-error-handling
+ test-fullshape
+ test-key2pho
+ test-keyboard
+ test-keyboardless
+ test-logger
+ test-mmap
+ test-path
+ test-regression
+ test-reset
+ test-special-symbol
+ test-struct-size
+ test-symbol
+ test-userphrase
+ test-utf8
+)
+set(ALL_TESTTOOLS
+ performance
+ randkeystroke
+ simulate
+ stress
+ testchewing
+)
+
+if(${CURSES_FOUND})
+ set(ALL_TESTTOOLS ${ALL_TESTTOOLS} genkeystroke)
+endif()
+
+
+set(ALL_TESTS ${ALL_TESTCASES} ${ALL_TESTTOOLS})
+
+foreach(target ${ALL_TESTCASES})
+ add_test(${target} ${TEST_BIN_DIR}/${target})
+endforeach()
+
+if(USE_VALGRIND)
+ find_program(VALGRIND valgrind)
+ if(VALGRIND)
+ foreach(target ${ALL_TESTCASES})
+ add_test("valgrind-${target}" ${VALGRIND} --error-exitcode=255 --leak-check=full ${TEST_BIN_DIR}/${target})
+ endforeach()
+ endif()
+endif()
+
+foreach(target ${ALL_TESTS})
+ add_executable(${target} ${TEST_SRC_DIR}/${target}.c)
+ add_dependencies(${target} data all_static_data)
+ add_dependencies(check ${target})
+endforeach()
+
+add_library(testhelper STATIC
+ ${TEST_SRC_DIR}/testhelper.c
+ $<TARGET_OBJECTS:chewing>
+ $<TARGET_OBJECTS:common>
+)
+target_link_libraries(testhelper userphrase)
+target_compile_definitions(testhelper PRIVATE
+ CHEWING_DATA_PREFIX=\"${DATA_BIN_DIR}\"
+ TEST_HASH_DIR=\"${TEST_BIN_DIR}\"
+ TEST_DATA_DIR=\"${TEST_SRC_DIR}/data\"
+ TESTDATA=\"${TEST_SRC_DIR}/default-test.txt\"
+)
+
+set_target_properties(${ALL_TESTS} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${TEST_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TEST_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TEST_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TEST_BIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${TEST_BIN_DIR}
+)
+foreach(target ${ALL_TESTS})
+ target_link_libraries(${target} testhelper)
+ target_compile_definitions(${target} PRIVATE
+ CHEWING_DATA_PREFIX=\"${DATA_BIN_DIR}\"
+ TEST_HASH_DIR=\"${TEST_BIN_DIR}\"
+ TEST_DATA_DIR=\"${TEST_SRC_DIR}/data\"
+ TESTDATA=\"${TEST_SRC_DIR}/default-test.txt\"
+ )
+endforeach()
+
+if (${CURSES_FOUND})
+ target_link_libraries(genkeystroke ${CURSES_LIBRARIES})
+endif()
+
+set(ALL_STATIC_TEST stresstest.py)
+foreach(target ${ALL_STATIC_TEST})
+ add_custom_target(${target} ALL
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${TEST_SRC_DIR}/${target} ${TEST_BIN_DIR}/${target}
+ )
+ add_dependencies(check ${target})
+endforeach()
\ No newline at end of file
--
2.39.1

Kan-Ru Chen

unread,
Jan 26, 2023, 8:42:48 PM1/26/23
to chewin...@googlegroups.com, czc...@czchen.org, jser...@gmail.com, Kan-Ru Chen
---
.gitignore | 3 +
.gitmodules | 3 +
CMakeLists.txt | 15 ++
Cargo.lock | 328 +++++++++++++++++++++++
Cargo.toml | 17 ++
capi/chewing-public/Cargo.toml | 15 ++
capi/chewing-public/build.rs | 8 +
capi/chewing-public/cbindgen.toml | 84 ++++++
capi/chewing-public/include/chewing_rs.h | 134 +++++++++
capi/chewing-public/src/io.rs | 18 ++
capi/chewing-public/src/lib.rs | 1 +
capi/chewing-public/src/types.rs | 47 ++++
cmake/corrosion | 1 +
include/chewing.h | 6 +-
include/chewingio.h | 6 +-
include/global.h | 24 --
include/internal/chewing-private.h | 23 +-
include/internal/hash-private.h | 1 -
include/internal/key2pho-private.h | 4 +-
include/mod_aux.h | 6 +-
src/chewingio.c | 1 -
src/chewingutil.c | 1 -
src/choice.c | 1 -
src/common/key2pho.c | 7 +-
src/lib.rs | 0
src/mod_aux.c | 1 -
src/tree.c | 1 -
test/test-bopomofo.c | 1 -
test/test-key2pho.c | 1 -
test/testhelper.h | 24 ++
30 files changed, 724 insertions(+), 58 deletions(-)
create mode 100644 .gitmodules
create mode 100644 Cargo.lock
create mode 100644 Cargo.toml
create mode 100644 capi/chewing-public/Cargo.toml
create mode 100644 capi/chewing-public/build.rs
create mode 100644 capi/chewing-public/cbindgen.toml
create mode 100644 capi/chewing-public/include/chewing_rs.h
create mode 100644 capi/chewing-public/src/io.rs
create mode 100644 capi/chewing-public/src/lib.rs
create mode 100644 capi/chewing-public/src/types.rs
create mode 160000 cmake/corrosion
create mode 100644 src/lib.rs

diff --git a/.gitignore b/.gitignore
index e917c79..017d159 100644
--- a/.gitignore
+++ b/.gitignore
@@ -145,3 +145,6 @@ contrib/test.sqlite3

# python
*.pyc
+
+# rust
+/target
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ab5575f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "cmake/corrosion"]
+ path = cmake/corrosion
+ url = https://github.com/corrosion-rs/corrosion.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 406089d..f3c6edf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -72,6 +72,13 @@ endif()

option(USE_VALGRIND "Use valgrind when testing" true)

+option(WITH_RUST "Use rust implemented internals (experimental)" true)
+if (WITH_RUST)
+ add_subdirectory(cmake/corrosion)
+ corrosion_import_crate(MANIFEST_PATH Cargo.toml)
+ add_compile_definitions(WITH_RUST)
+endif()
+
# Feature probe
include(CheckTypeSize)
check_type_size(uint16_t UINT16_T)
@@ -159,6 +166,11 @@ set(ALL_INC
${INC_DIR}/mod_aux.h
)

+if (WITH_RUST)
+ include_directories(capi/chewing-public/include)
+ list(APPEND ALL_INC capi/chewing-public/include/chewing_rs.h)
+endif()
+
add_subdirectory(doc)
add_subdirectory(test)
add_subdirectory(data)
@@ -276,6 +288,9 @@ foreach(lib ${LIBS})
if (WITH_SQLITE3 AND NOT WITH_INTERNAL_SQLITE3)
target_link_libraries(${lib} ${SQLITE3_LIBRARY})
endif()
+ if (WITH_RUST)
+ target_link_libraries(${lib} chewing-public)
+ endif()
endforeach()

set_target_properties(${LIBS} PROPERTIES
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..8fcb491
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,328 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cbindgen"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb"
+dependencies = [
+ "clap",
+ "heck",
+ "indexmap",
+ "log",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "syn",
+ "tempfile",
+ "toml",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chewing"
+version = "0.5.1-alpha.1"
+
+[[package]]
+name = "chewing-public"
+version = "0.5.1-alpha.1"
+dependencies = [
+ "cbindgen",
+ "chewing",
+]
+
+[[package]]
+name = "clap"
+version = "3.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_lex",
+ "indexmap",
+ "strsim",
+ "termcolor",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
+
+[[package]]
+name = "libc"
+version = "0.2.139"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
+
+[[package]]
+name = "serde"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..dce7216
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "chewing"
+description = "The Chewing (酷音) intelligent Zhuyin input method."
+license = "LGPL-2.1-or-later"
+documentation = "https://docs.rs/chewing"
+version = "0.5.1-alpha.1"
+edition = "2021"
+
+[dependencies]
+
+[dev-dependencies]
+
+[workspace]
+members = ["capi/chewing-public"]
+
+[profile.release]
+lto = "thin"
\ No newline at end of file
diff --git a/capi/chewing-public/Cargo.toml b/capi/chewing-public/Cargo.toml
new file mode 100644
index 0000000..69c32f9
--- /dev/null
+++ b/capi/chewing-public/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "chewing-public"
+description = "Public C API for the Chewing (酷音) intelligent Zhuyin input method."
+license = "LGPL-2.1-or-later"
+version = "0.5.1-alpha.1"
+edition = "2021"
+
+[dependencies]
+chewing = { version = "0.5.1-alpha.1", path = "../.." }
+
+[lib]
+crate-type = ["staticlib"]
+
+[build-dependencies]
+cbindgen = "0.24.3"
\ No newline at end of file
diff --git a/capi/chewing-public/build.rs b/capi/chewing-public/build.rs
new file mode 100644
index 0000000..00fe0e0
--- /dev/null
+++ b/capi/chewing-public/build.rs
@@ -0,0 +1,8 @@
+use std::env;
+
+fn main() {
+ let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+ cbindgen::generate(crate_dir)
+ .expect("Unable to generate C headers for Rust code")
+ .write_to_file("include/chewing_rs.h");
+}
diff --git a/capi/chewing-public/cbindgen.toml b/capi/chewing-public/cbindgen.toml
new file mode 100644
index 0000000..16c1083
--- /dev/null
+++ b/capi/chewing-public/cbindgen.toml
@@ -0,0 +1,84 @@
+# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
+# for detailed documentation of every option here.
+
+language = "C"
+
+############## Options for Wrapping the Contents of the Header #################
+
+header = """
+/*
+ * Copyright (c) 2023
+ * libchewing Core Team. See ChangeLog for details.
+ *
+ * See the file "COPYING" for information on usage and redistribution
+ * of this file.
+ */"""
+autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
+include_guard = "chewing_public_bindings_h"
+pragma_once = true
+cpp_compat = true
+after_includes = """
+
+/** @brief context handle used for Chewing IM APIs
+ */
+typedef struct ChewingContext ChewingContext;
+
+/* specified to Chewing API */
+#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
+# define CHEWING_DLL_IMPORT __declspec(dllimport)
+# define CHEWING_DLL_EXPORT __declspec(dllexport)
+# ifdef CHEWINGDLL_EXPORTS
+# define CHEWING_API CHEWING_DLL_EXPORT
+# define CHEWING_PRIVATE
+# elif CHEWINGDLL_IMPORTS
+# define CHEWING_API CHEWING_DLL_IMPORT
+# define CHEWING_PRIVATE
+# else
+# define CHEWING_API
+# define CHEWING_PRIVATE
+# endif
+#elif (__GNUC__ > 3) && (defined(__ELF__) || defined(__PIC__))
+# define CHEWING_API __attribute__((__visibility__("default")))
+# define CHEWING_PRIVATE __attribute__((__visibility__("hidden")))
+#else
+# define CHEWING_API
+# define CHEWING_PRIVATE
+#endif
+
+#ifndef UNUSED
+# if defined(__GNUC__) /* gcc specific */
+# define UNUSED __attribute__((unused))
+# else
+# define UNUSED
+# endif
+#endif
+
+#ifndef DEPRECATED
+# if defined(__GNUC__) && __GNUC__ > 3 || \
+ (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) /* gcc specific */
+# define DEPRECATED __attribute__((deprecated))
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# define DEPRECATED_FOR(f) \
+ __attribute__((deprecated("Use " #f " instead")))
+# else
+# define DEPRECATED_FOR(f) DEPRECATED
+# endif
+# else
+# define DEPRECATED
+# define DEPRECATED_FOR(f)
+# endif
+#endif
+"""
+
+[parse]
+parse_deps = true
+include = ["chewing"]
+
+[struct]
+rename_fields = "CamelCase"
+
+[enum]
+rename_variants = "ScreamingSnakeCase"
+
+[export]
+include = ["KB", "ChewingConfigData", "IntervalType", "ChewingContext"]
diff --git a/capi/chewing-public/include/chewing_rs.h b/capi/chewing-public/include/chewing_rs.h
new file mode 100644
index 0000000..191f368
--- /dev/null
+++ b/capi/chewing-public/include/chewing_rs.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2023
+ * libchewing Core Team. See ChangeLog for details.
+ *
+ * See the file "COPYING" for information on usage and redistribution
+ * of this file.
+ */
+
+#ifndef chewing_public_bindings_h
+#define chewing_public_bindings_h
+
+#pragma once
+
+/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/** @brief context handle used for Chewing IM APIs
+ */
+typedef struct ChewingContext ChewingContext;
+
+/* specified to Chewing API */
+#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
+# define CHEWING_DLL_IMPORT __declspec(dllimport)
+# define CHEWING_DLL_EXPORT __declspec(dllexport)
+# ifdef CHEWINGDLL_EXPORTS
+# define CHEWING_API CHEWING_DLL_EXPORT
+# define CHEWING_PRIVATE
+# elif CHEWINGDLL_IMPORTS
+# define CHEWING_API CHEWING_DLL_IMPORT
+# define CHEWING_PRIVATE
+# else
+# define CHEWING_API
+# define CHEWING_PRIVATE
+# endif
+#elif (__GNUC__ > 3) && (defined(__ELF__) || defined(__PIC__))
+# define CHEWING_API __attribute__((__visibility__("default")))
+# define CHEWING_PRIVATE __attribute__((__visibility__("hidden")))
+#else
+# define CHEWING_API
+# define CHEWING_PRIVATE
+#endif
+
+#ifndef UNUSED
+# if defined(__GNUC__) /* gcc specific */
+# define UNUSED __attribute__((unused))
+# else
+# define UNUSED
+# endif
+#endif
+
+#ifndef DEPRECATED
+# if defined(__GNUC__) && __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) /* gcc specific */
+# define DEPRECATED __attribute__((deprecated))
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# define DEPRECATED_FOR(f) __attribute__((deprecated("Use " #f " instead")))
+# else
+# define DEPRECATED_FOR(f) DEPRECATED
+# endif
+# else
+# define DEPRECATED
+# define DEPRECATED_FOR(f)
+# endif
+#endif
+
+
+#define CHINESE_MODE 1
+
+#define SYMBOL_MODE 0
+
+#define FULLSHAPE_MODE 1
+
+#define HALFSHAPE_MODE 0
+
+#define AUTOLEARN_DISABLED 1
+
+#define AUTOLEARN_ENABLED 0
+
+#define MIN_SELKEY 1
+
+#define MAX_SELKEY 10
+
+#define CHEWING_LOG_VERBOSE 1
+
+#define CHEWING_LOG_DEBUG 2
+
+#define CHEWING_LOG_INFO 3
+
+#define CHEWING_LOG_WARN 4
+
+#define CHEWING_LOG_ERROR 5
+
+/**
+ * Use "asdfjkl789" as selection key
+ */
+#define HSU_SELKEY_TYPE1 1
+
+/**
+ * Use "asdfzxcv89" as selection key
+ */
+#define HSU_SELKEY_TYPE2 2
+
+/**
+ * Deprecated, use chewing_set_ series of functions to set parameters instead.
+ */
+typedef struct ChewingConfigData {
+ int candPerPage;
+ int maxChiSymbolLen;
+ int selKey[MAX_SELKEY];
+ int bAddPhraseForward;
+ int bSpaceAsSelection;
+ int bEscCleanAllBuf;
+ int bAutoShiftCur;
+ int bEasySymbolInput;
+ int bPhraseChoiceRearward;
+ int bAutoLearn;
+ int hsuSelKeyType;
+} ChewingConfigData;
+
+typedef struct IntervalType {
+ /**
+ * Starting position of certain interval
+ */
+ int from;
+ /**
+ * Ending position of certain interval (exclusive)
+ */
+ int to;
+} IntervalType;
+
+#endif /* chewing_public_bindings_h */
diff --git a/capi/chewing-public/src/io.rs b/capi/chewing-public/src/io.rs
new file mode 100644
index 0000000..a80e5cc
--- /dev/null
+++ b/capi/chewing-public/src/io.rs
@@ -0,0 +1,18 @@
+use std::ffi::c_int;
+
+use crate::capi::internal::{binding, types::ChewingContext};
+
+#[no_mangle]
+pub extern "C" fn chewing_new() -> *mut ChewingContext {
+ unsafe { binding::chewing_new_c().cast() }
+}
+
+#[no_mangle]
+pub extern "C" fn chewing_set_maxChiSymbolLen(ctx: *mut ChewingContext, n: c_int) {
+ unsafe { binding::chewing_set_maxChiSymbolLen_c(ctx.cast(), n) }
+}
+
+#[no_mangle]
+pub extern "C" fn chewing_delete(ctx: *mut ChewingContext) {
+ unsafe { binding::chewing_delete_c(ctx.cast()) }
+}
\ No newline at end of file
diff --git a/capi/chewing-public/src/lib.rs b/capi/chewing-public/src/lib.rs
new file mode 100644
index 0000000..dd198c6
--- /dev/null
+++ b/capi/chewing-public/src/lib.rs
@@ -0,0 +1 @@
+pub mod types;
\ No newline at end of file
diff --git a/capi/chewing-public/src/types.rs b/capi/chewing-public/src/types.rs
new file mode 100644
index 0000000..d51fb9a
--- /dev/null
+++ b/capi/chewing-public/src/types.rs
@@ -0,0 +1,47 @@
+use std::ffi::c_int;
+
+pub const CHINESE_MODE: usize = 1;
+pub const SYMBOL_MODE: usize = 0;
+pub const FULLSHAPE_MODE: usize = 1;
+pub const HALFSHAPE_MODE: usize = 0;
+pub const AUTOLEARN_DISABLED: usize = 1;
+pub const AUTOLEARN_ENABLED: usize = 0;
+
+pub const MIN_SELKEY: usize = 1;
+pub const MAX_SELKEY: usize = 10;
+
+pub const CHEWING_LOG_VERBOSE: usize = 1;
+pub const CHEWING_LOG_DEBUG: usize = 2;
+pub const CHEWING_LOG_INFO: usize = 3;
+pub const CHEWING_LOG_WARN: usize = 4;
+pub const CHEWING_LOG_ERROR: usize = 5;
+
+/// Use "asdfjkl789" as selection key
+pub const HSU_SELKEY_TYPE1: usize = 1;
+/// Use "asdfzxcv89" as selection key
+pub const HSU_SELKEY_TYPE2: usize = 2;
+
+/// Deprecated, use chewing_set_ series of functions to set parameters instead.
+/// cbindgen:rename-all=CamelCase
+#[repr(C)]
+pub struct ChewingConfigData {
+ pub cand_per_page: c_int,
+ pub max_chi_symbol_len: c_int,
+ pub sel_key: [c_int; MAX_SELKEY],
+ pub b_add_phrase_forward: c_int,
+ pub b_space_as_selection: c_int,
+ pub b_esc_clean_all_buf: c_int,
+ pub b_auto_shift_cur: c_int,
+ pub b_easy_symbol_input: c_int,
+ pub b_phrase_choice_rearward: c_int,
+ pub b_auto_learn: c_int,
+ pub hsu_sel_key_type: c_int,
+}
+
+#[repr(C)]
+pub struct IntervalType {
+ /// Starting position of certain interval
+ pub from: c_int,
+ /// Ending position of certain interval (exclusive)
+ pub to: c_int,
+}
diff --git a/cmake/corrosion b/cmake/corrosion
new file mode 160000
index 0000000..e3d8d20
--- /dev/null
+++ b/cmake/corrosion
@@ -0,0 +1 @@
+Subproject commit e3d8d200082fd49350dc6b9c4f974efc2402f6ae
diff --git a/include/chewing.h b/include/chewing.h
index 8faceb7..08dc23f 100644
--- a/include/chewing.h
+++ b/include/chewing.h
@@ -44,8 +44,12 @@ extern "C" {
* specific functionality.
*/

+#ifdef WITH_RUST
+# include "chewing_rs.h"
+#else
+# include "global.h"
+#endif
#include "chewingio.h"
-#include "global.h"
#include "mod_aux.h"

/* backward compatibility */
diff --git a/include/chewingio.h b/include/chewingio.h
index de231cf..10e471d 100644
--- a/include/chewingio.h
+++ b/include/chewingio.h
@@ -22,7 +22,11 @@
* \author libchewing Core Team
*/

-#include "global.h"
+#ifdef WITH_RUST
+# include "chewing_rs.h"
+#else
+# include "global.h"
+#endif

#define KEYSTROKE_IGNORE 1
#define KEYSTROKE_COMMIT 2
diff --git a/include/global.h b/include/global.h
index 204cfa8..6907484 100644
--- a/include/global.h
+++ b/include/global.h
@@ -75,30 +75,6 @@
# endif
#endif

-/* The following macros are modified from GLIB.
- * from GNU cpp Manual:
- * C99 introduces the _Pragma operator. This feature addresses a major problem
- * with `#pragma': being a directive, it cannot be produced as the result of
- * macro expansion. _Pragma is an operator, much like sizeof or defined, and
- * can be embedded in a macro.
- */
-#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
-# define BEGIN_IGNORE_DEPRECATIONS \
- _Pragma ("GCC diagnostic push") \
- _Pragma ("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
-# define END_IGNORE_DEPRECATIONS \
- _Pragma ("GCC diagnostic pop")
-#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
-# define BEGIN_IGNORE_DEPRECATIONS \
- __pragma (warning (push)) \
- __pragma (warning (disable : 4996))
-# define END_IGNORE_DEPRECATIONS \
- __pragma (warning (pop))
-#else
-# define BEGIN_IGNORE_DEPRECATIONS
-# define END_IGNORE_DEPRECATIONS
-#endif
-
#define MIN_SELKEY 1
#define MAX_SELKEY 10

diff --git a/include/internal/chewing-private.h b/include/internal/chewing-private.h
index 260a0a7..17762f4 100644
--- a/include/internal/chewing-private.h
+++ b/include/internal/chewing-private.h
@@ -29,7 +29,11 @@
typedef SSIZE_T ssize_t;
#endif

-#include "global.h"
+#ifdef WITH_RUST
+# include "chewing_rs.h"
+#else
+# include "global.h"
+#endif
#include "plat_mmap.h"

#include "userphrase-private.h"
@@ -73,23 +77,6 @@ static inline int min(int a, int b)
}
#endif

-typedef enum KBTYPE {
- KBTYPE_STANDARD,
- KBTYPE_HSU,
- KBTYPE_IBM,
- KBTYPE_GIN_YIEH,
- KBTYPE_ET,
- KBTYPE_ET26,
- KBTYPE_DVORAK,
- KBTYPE_DVORAK_HSU,
- KBTYPE_DACHEN_CP26,
- KBTYPE_HANYU_PINYIN,
- KBTYPE_LUOMA_PINYIN,
- KBTYPE_MSP2, /* Mandarin Phonetic Symbols II */
- KBTYPE_CARPALX,
- KBTYPE_COUNT
-} KBTYPE;
-
/**
* @struct TreeType
* @brief node type of the system index tree
diff --git a/include/internal/hash-private.h b/include/internal/hash-private.h
index 09150e2..4a21f76 100644
--- a/include/internal/hash-private.h
+++ b/include/internal/hash-private.h
@@ -13,7 +13,6 @@
#define _CHEWING_HASH_PRIVATE_H
/* *INDENT-ON* */

-#include "global.h"
#include "userphrase-private.h"

#ifdef __MacOSX__
diff --git a/include/internal/key2pho-private.h b/include/internal/key2pho-private.h
index 9bb8fb7..b5dce61 100644
--- a/include/internal/key2pho-private.h
+++ b/include/internal/key2pho-private.h
@@ -79,7 +79,7 @@ uint16_t UintFromPhoneInx(const int ph_inx[]);
* @param[in] searchTimes times to search.
* @return 1 if succeed or 0 if failed.
*/
-int PhoneFromKey(char *pho, const char *inputkey, KBTYPE kbtype, int searchTimes);
+int PhoneFromKey(char *pho, const char *inputkey, int kbtype, int searchTimes);

/**
* @brief Get the string of phonetic symbols by its phonetic number.
@@ -97,7 +97,7 @@ int PhoneFromUint(char *phone, size_t phone_len, uint16_t phone_num);
* @param[in]
* @return
*/
-int PhoneInxFromKey(int key, int type, KBTYPE kbtype, int searchTimes);
+int PhoneInxFromKey(int key, int type, int kbtype, int searchTimes);

/**
* @brief
diff --git a/include/mod_aux.h b/include/mod_aux.h
index 96aa244..650c53d 100644
--- a/include/mod_aux.h
+++ b/include/mod_aux.h
@@ -18,7 +18,11 @@
* \author libchewing Core Team
*/

-#include "global.h"
+#ifdef WITH_RUST
+# include "chewing_rs.h"
+#else
+# include "global.h"
+#endif

/**
* @brief Chewing the state for input context during commit process
diff --git a/src/chewingio.c b/src/chewingio.c
index 5e6c285..3e37122 100644
--- a/src/chewingio.c
+++ b/src/chewingio.c
@@ -27,7 +27,6 @@
#include <stdio.h>

#include "chewing-utf8-util.h"
-#include "global.h"
#include "bopomofo-private.h"
#include "chewingutil.h"
#include "userphrase-private.h"
diff --git a/src/chewingutil.c b/src/chewingutil.c
index 01f6b6c..8344285 100644
--- a/src/chewingutil.c
+++ b/src/chewingutil.c
@@ -24,7 +24,6 @@
#include <stdio.h>

#include "chewing-utf8-util.h"
-#include "global.h"
#include "global-private.h"
#include "chewingutil.h"
#include "bopomofo-private.h"
diff --git a/src/choice.c b/src/choice.c
index b832bed..7759ab2 100644
--- a/src/choice.c
+++ b/src/choice.c
@@ -21,7 +21,6 @@
#include <assert.h>

#include "chewing-utf8-util.h"
-#include "global.h"
#include "dict-private.h"
#include "chewingutil.h"
#include "tree-private.h"
diff --git a/src/common/key2pho.c b/src/common/key2pho.c
index 85d94a6..7f74264 100644
--- a/src/common/key2pho.c
+++ b/src/common/key2pho.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <string.h>
#include "chewing-utf8-util.h"
+#include "bopomofo-private.h"

/* NOTE:
* The reason why we convert string literal to hex representation is for the
@@ -69,7 +70,7 @@ static const char *const ph_str =
"\xCB\x99\xCB\x8A\xCB\x87\xCB\x8B";
/* ˙ˊˇˋ */

-static const char *const key_str[KBTYPE_COUNT] = {
+static const char *const key_str[KB_TYPE_NUM] = {
"1qaz2wsxedcrfv5tgbyhnujm8ik,9ol.0p;/-7634", /* standard kb */
"bpmfdtnlgkhjvcjvcrzasexuyhgeiawomnkllsdfj", /* hsu */
"1234567890-qwertyuiopasdfghjkl;zxcvbn/m,.", /* IBM */
@@ -122,7 +123,7 @@ uint16_t UintFromPhone(const char *zhuin)
return result;
}

-int PhoneFromKey(char *pho, const char *inputkey, KBTYPE kbtype, int searchTimes)
+int PhoneFromKey(char *pho, const char *inputkey, int kbtype, int searchTimes)
{
int len;
int i;
@@ -181,7 +182,7 @@ int PhoneFromUint(char *phone, size_t phone_len, uint16_t phone_num)
return 0;
}

-int PhoneInxFromKey(int key, int type, KBTYPE kbtype, int searchTimes)
+int PhoneInxFromKey(int key, int type, int kbtype, int searchTimes)
{
char keyStr[2];
char rtStr[10];
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..e69de29
diff --git a/src/mod_aux.c b/src/mod_aux.c
index ce9c7cb..7818f8d 100644
--- a/src/mod_aux.c
+++ b/src/mod_aux.c
@@ -16,7 +16,6 @@
#include <string.h>
#include <stdlib.h>

-#include "global.h"
#include "chewing-private.h"
#include "bopomofo-private.h"
#include "chewingio.h"
diff --git a/src/tree.c b/src/tree.c
index 84ddffc..f6d70dc 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -24,7 +24,6 @@
#include "chewing-private.h"
#include "chewing-utf8-util.h"
#include "userphrase-private.h"
-#include "global.h"
#include "global-private.h"
#include "dict-private.h"
#include "memory-private.h"
diff --git a/test/test-bopomofo.c b/test/test-bopomofo.c
index 2b1e6b9..37e01f2 100644
--- a/test/test-bopomofo.c
+++ b/test/test-bopomofo.c
@@ -19,7 +19,6 @@

#include "key2pho-private.h"
#include "bopomofo-private.h"
-#include "chewing.h"
#include "plat_types.h"
#include "testhelper.h"

diff --git a/test/test-key2pho.c b/test/test-key2pho.c
index 324268f..eb83a8c 100644
--- a/test/test-key2pho.c
+++ b/test/test-key2pho.c
@@ -13,7 +13,6 @@
#include <stdlib.h>

#include "testhelper.h"
-#include "global.h"
#include "chewing-utf8-util.h"
#include "key2pho-private.h"
#include "chewing-private.h"
diff --git a/test/testhelper.h b/test/testhelper.h
index 58c734a..dc2f2f7 100644
--- a/test/testhelper.h
+++ b/test/testhelper.h
@@ -19,6 +19,30 @@

#include "plat_path.h"

+/* The following macros are modified from GLIB.
+ * from GNU cpp Manual:
+ * C99 introduces the _Pragma operator. This feature addresses a major problem
+ * with `#pragma': being a directive, it cannot be produced as the result of
+ * macro expansion. _Pragma is an operator, much like sizeof or defined, and
+ * can be embedded in a macro.
+ */
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
+# define BEGIN_IGNORE_DEPRECATIONS \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+# define END_IGNORE_DEPRECATIONS \
+ _Pragma ("GCC diagnostic pop")
+#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
+# define BEGIN_IGNORE_DEPRECATIONS \
+ __pragma (warning (push)) \
+ __pragma (warning (disable : 4996))
+# define END_IGNORE_DEPRECATIONS \
+ __pragma (warning (pop))
+#else
+# define BEGIN_IGNORE_DEPRECATIONS
+# define END_IGNORE_DEPRECATIONS
+#endif
+
#define KEY_DBLTAB 892 // <TT>
#define KEY_SSPACE 893 // <SS>
#define KEY_PPAGE 894 // <PU>
--
2.39.1

Kan-Ru Chen

unread,
Jan 26, 2023, 8:42:51 PM1/26/23
to chewin...@googlegroups.com, czc...@czchen.org, jser...@gmail.com, Kan-Ru Chen
---
CMakeLists.txt | 2 +
Cargo.lock | 31 ++
Cargo.toml | 5 +-
capi/chewing-internal/Cargo.toml | 15 +
capi/chewing-internal/build.rs | 8 +
capi/chewing-internal/cbindgen.toml | 34 ++
.../include/chewing_internal.h | 40 ++
capi/chewing-internal/src/key2pho.rs | 128 +++++
capi/chewing-internal/src/lib.rs | 1 +
data/CMakeLists.txt | 9 +-
include/internal/chewing-private.h | 1 +
include/internal/key2pho-private.h | 9 +-
src/common/key2pho.c | 15 +
src/lib.rs | 1 +
src/zhuyin.rs | 5 +
src/zhuyin/bopomofo.rs | 298 +++++++++++
src/zhuyin/syllable.rs | 485 ++++++++++++++++++
test/CMakeLists.txt | 3 +
18 files changed, 1086 insertions(+), 4 deletions(-)
create mode 100644 capi/chewing-internal/Cargo.toml
create mode 100644 capi/chewing-internal/build.rs
create mode 100644 capi/chewing-internal/cbindgen.toml
create mode 100644 capi/chewing-internal/include/chewing_internal.h
create mode 100644 capi/chewing-internal/src/key2pho.rs
create mode 100644 capi/chewing-internal/src/lib.rs
create mode 100644 src/zhuyin.rs
create mode 100644 src/zhuyin/bopomofo.rs
create mode 100644 src/zhuyin/syllable.rs

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f3c6edf..76c155f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -167,6 +167,7 @@ set(ALL_INC
)

if (WITH_RUST)
+ include_directories(capi/chewing-internal/include)
include_directories(capi/chewing-public/include)
list(APPEND ALL_INC capi/chewing-public/include/chewing_rs.h)
endif()
@@ -290,6 +291,7 @@ foreach(lib ${LIBS})
endif()
if (WITH_RUST)
target_link_libraries(${lib} chewing-public)
+ target_link_libraries(${lib} chewing-internal)
endif()
endforeach()

diff --git a/Cargo.lock b/Cargo.lock
index 8fcb491..49659ae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -53,6 +53,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chewing"
version = "0.5.1-alpha.1"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "chewing-internal"
+version = "0.5.1-alpha.1"
+dependencies = [
+ "cbindgen",
+ "chewing",
+]

[[package]]
name = "chewing-public"
@@ -281,6 +292,26 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"

+[[package]]
+name = "thiserror"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "toml"
version = "0.5.11"
diff --git a/Cargo.toml b/Cargo.toml
index dce7216..4db5cf4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,11 +7,12 @@ version = "0.5.1-alpha.1"
edition = "2021"

[dependencies]
+thiserror = "1.0.0"

[dev-dependencies]

[workspace]
-members = ["capi/chewing-public"]
+members = ["capi/chewing-internal", "capi/chewing-public"]

[profile.release]
-lto = "thin"
\ No newline at end of file
+lto = "thin"
diff --git a/capi/chewing-internal/Cargo.toml b/capi/chewing-internal/Cargo.toml
new file mode 100644
index 0000000..dd53c9b
--- /dev/null
+++ b/capi/chewing-internal/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "chewing-internal"
+description = "Internal C API for the Chewing (酷音) intelligent Zhuyin input method."
+license = "LGPL-2.1-or-later"
+version = "0.5.1-alpha.1"
+edition = "2021"
+
+[dependencies]
+chewing = { version = "0.5.1-alpha.1", path = "../.." }
+
+[lib]
+crate-type = ["staticlib"]
+
+[build-dependencies]
+cbindgen = "0.24.3"
diff --git a/capi/chewing-internal/build.rs b/capi/chewing-internal/build.rs
new file mode 100644
index 0000000..e1a2112
--- /dev/null
+++ b/capi/chewing-internal/build.rs
@@ -0,0 +1,8 @@
+use std::env;
+
+fn main() {
+ let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+ cbindgen::generate(crate_dir)
+ .expect("Unable to generate C headers for Rust code")
+ .write_to_file("include/chewing_internal.h");
+}
diff --git a/capi/chewing-internal/cbindgen.toml b/capi/chewing-internal/cbindgen.toml
new file mode 100644
index 0000000..af85092
--- /dev/null
+++ b/capi/chewing-internal/cbindgen.toml
@@ -0,0 +1,34 @@
+# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
+# for detailed documentation of every option here.
+
+language = "C"
+
+############## Options for Wrapping the Contents of the Header #################
+
+header = """
+/*
+ * Copyright (c) 2022
+ * libchewing Core Team. See ChangeLog for details.
+ *
+ * See the file "COPYING" for information on usage and redistribution
+ * of this file.
+ */"""
+include_guard = "chewing_internal_bindings_h"
+autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
+pragma_once = true
+include_version = false
+cpp_compat = true
+usize_is_size_t = true
+
+[parse]
+parse_deps = true
+include = ["chewing"]
+
+[struct]
+rename_fields = "CamelCase"
+
+[enum]
+rename_variants = "ScreamingSnakeCase"
+
+[export]
+include = []
diff --git a/capi/chewing-internal/include/chewing_internal.h b/capi/chewing-internal/include/chewing_internal.h
new file mode 100644
index 0000000..3d7bbbf
--- /dev/null
+++ b/capi/chewing-internal/include/chewing_internal.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022
+ * libchewing Core Team. See ChangeLog for details.
+ *
+ * See the file "COPYING" for information on usage and redistribution
+ * of this file.
+ */
+
+#ifndef chewing_internal_bindings_h
+#define chewing_internal_bindings_h
+
+#pragma once
+
+/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+uint16_t UintFromPhone(const char *phone);
+
+uint16_t UintFromPhoneInx(const int *ph_inx);
+
+int PhoneFromUint(char *phone, size_t phone_len, uint16_t phone_num);
+
+ptrdiff_t UintArrayFromBopomofo(uint16_t *phone_seq, size_t phone_len, const char *bopomofo_buf);
+
+int GetPhoneLenFromUint(uint16_t phone_num);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif /* chewing_internal_bindings_h */
diff --git a/capi/chewing-internal/src/key2pho.rs b/capi/chewing-internal/src/key2pho.rs
new file mode 100644
index 0000000..270bb65
--- /dev/null
+++ b/capi/chewing-internal/src/key2pho.rs
@@ -0,0 +1,128 @@
+use std::{
+ ffi::{c_char, c_int, CStr},
+ ptr, slice,
+};
+
+use chewing::zhuyin::{Bopomofo, Syllable};
+
+#[no_mangle]
+pub extern "C" fn UintFromPhone(phone: *const c_char) -> u16 {
+ let cstr = unsafe { CStr::from_ptr(phone) };
+ let rstr = match cstr.to_str() {
+ Ok(rstr) => rstr,
+ Err(_) => return 0,
+ };
+ let syl: Syllable = match rstr.parse() {
+ Ok(syl) => syl,
+ Err(_) => return 0,
+ };
+ syl.to_u16()
+}
+
+#[no_mangle]
+pub extern "C" fn UintFromPhoneInx(ph_inx: *const c_int) -> u16 {
+ let ph_inx = unsafe { slice::from_raw_parts(ph_inx, 4) };
+ let mut builder = Syllable::builder();
+ if ph_inx[0] > 0 {
+ let bopomofo = match Bopomofo::from_initial(ph_inx[0] as u16) {
+ Ok(bopomofo) => bopomofo,
+ Err(_) => return 0,
+ };
+ builder = match builder.insert(bopomofo) {
+ Ok(builder) => builder,
+ Err(_) => return 0,
+ };
+ }
+ if ph_inx[1] > 0 {
+ let bopomofo = match Bopomofo::from_medial(ph_inx[1] as u16) {
+ Ok(bopomofo) => bopomofo,
+ Err(_) => return 0,
+ };
+ builder = match builder.insert(bopomofo) {
+ Ok(builder) => builder,
+ Err(_) => return 0,
+ };
+ }
+ if ph_inx[2] > 0 {
+ let bopomofo = match Bopomofo::from_rime(ph_inx[2] as u16) {
+ Ok(bopomofo) => bopomofo,
+ Err(_) => return 0,
+ };
+ builder = match builder.insert(bopomofo) {
+ Ok(builder) => builder,
+ Err(_) => return 0,
+ };
+ }
+ if ph_inx[3] > 0 {
+ let bopomofo = match Bopomofo::from_tone(ph_inx[3] as u16) {
+ Ok(bopomofo) => bopomofo,
+ Err(_) => return 0,
+ };
+ builder = match builder.insert(bopomofo) {
+ Ok(builder) => builder,
+ Err(_) => return 0,
+ };
+ }
+ let syl = builder.build();
+ if syl.is_empty() {
+ return 0;
+ }
+ syl.to_u16()
+}
+
+#[no_mangle]
+pub extern "C" fn PhoneFromUint(phone: *mut c_char, phone_len: usize, phone_num: u16) -> c_int {
+ let syl = match Syllable::try_from(phone_num) {
+ Ok(syl) => syl,
+ Err(_) => return 1,
+ };
+ let str = syl.to_string();
+ if phone_len < str.len() + 1 {
+ return 1;
+ }
+ unsafe {
+ phone.copy_from(str.as_ptr() as *const c_char, str.len());
+ ptr::write(phone.offset(str.len() as isize), 0);
+ }
+ 0
+}
+
+#[no_mangle]
+pub extern "C" fn UintArrayFromBopomofo(
+ phone_seq: *mut u16,
+ phone_len: usize,
+ bopomofo_buf: *const c_char,
+) -> isize {
+ let syllables_str = match unsafe { CStr::from_ptr(bopomofo_buf) }.to_str() {
+ Ok(str) => str,
+ Err(_) => return -1,
+ };
+ let syllables: Vec<_> = syllables_str
+ .split_ascii_whitespace()
+ .map(|it| it.parse::<Syllable>().map(|syl| syl.to_u16()))
+ .collect();
+ let len = syllables.len();
+ if syllables.iter().any(|it| it.is_err()) {
+ return -1;
+ }
+ if len > phone_len || phone_seq.is_null() {
+ return len as isize;
+ }
+ for (i, syl) in syllables.into_iter().enumerate() {
+ let syl_u16 = syl.unwrap();
+ unsafe { ptr::write(phone_seq.offset(i as isize), syl_u16) };
+ }
+ len as isize
+}
+
+#[no_mangle]
+pub extern "C" fn GetPhoneLenFromUint(phone_num: u16) -> c_int {
+ let syl = match Syllable::try_from(phone_num) {
+ Ok(syl) => syl,
+ Err(_) => return -1,
+ };
+ if syl.is_empty() {
+ return -1;
+ }
+ (syl.to_string().len() + 1) as c_int
+}
diff --git a/capi/chewing-internal/src/lib.rs b/capi/chewing-internal/src/lib.rs
new file mode 100644
index 0000000..e9c322e
--- /dev/null
+++ b/capi/chewing-internal/src/lib.rs
@@ -0,0 +1 @@
+pub mod key2pho;
\ No newline at end of file
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
index 4cb0068..aefc78a 100644
--- a/data/CMakeLists.txt
+++ b/data/CMakeLists.txt
@@ -19,8 +19,14 @@ add_custom_target(all_static_data
)

# tools
-set(ALL_TOOLS init_database dump_database)
+set(ALL_TOOLS init_database)
add_executable(init_database ${TOOLS_SRC_DIR}/init_database.c $<TARGET_OBJECTS:common>)
+if (WITH_RUST)
+target_link_libraries(init_database chewing-internal)
+endif()
+
+if (NOT WITH_RUST)
+list(APPEND ALL_TOOLS dump_database)
add_executable(dump_database
${TOOLS_SRC_DIR}/dump_database.c
${SRC_DIR}/porting_layer/src/plat_mmap_posix.c
@@ -28,6 +34,7 @@ add_executable(dump_database
${SRC_DIR}/porting_layer/src/rpl_malloc.c
$<TARGET_OBJECTS:common>
)
+endif()
set_target_properties(${ALL_TOOLS} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${TOOLS_BIN_DIR}
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TOOLS_BIN_DIR}
diff --git a/include/internal/chewing-private.h b/include/internal/chewing-private.h
index 17762f4..2c3a7ab 100644
--- a/include/internal/chewing-private.h
+++ b/include/internal/chewing-private.h
@@ -31,6 +31,7 @@ typedef SSIZE_T ssize_t;

#ifdef WITH_RUST
# include "chewing_rs.h"
+# include "chewing_internal.h"
#else
# include "global.h"
#endif
diff --git a/include/internal/key2pho-private.h b/include/internal/key2pho-private.h
index b5dce61..b26cedb 100644
--- a/include/internal/key2pho-private.h
+++ b/include/internal/key2pho-private.h
@@ -33,6 +33,7 @@ typedef SSIZE_T ssize_t;

#include "chewing-private.h"

+#ifndef WITH_RUST
/**
* @brief Get the unsigned 16-bit representation of phonetic symbols.
*
@@ -70,6 +71,7 @@ uint16_t UintFromPhone(const char *phone);
* @return an 16-bit unsigned integer or 0 if any index is illegal.
*/
uint16_t UintFromPhoneInx(const int ph_inx[]);
+#endif

/**
* @brief Get the phonetic symbols by the given keystroke.
@@ -81,6 +83,7 @@ uint16_t UintFromPhoneInx(const int ph_inx[]);
*/
int PhoneFromKey(char *pho, const char *inputkey, int kbtype, int searchTimes);

+#ifndef WITH_RUST
/**
* @brief Get the string of phonetic symbols by its phonetic number.
* @param[out] phone destination string.
@@ -89,6 +92,7 @@ int PhoneFromKey(char *pho, const char *inputkey, int kbtype, int searchTimes);
* @return 1 if succeed or 0 if failed.
*/
int PhoneFromUint(char *phone, size_t phone_len, uint16_t phone_num);
+#endif

/**
* @brief Get the index of a phonetic symbols in its category.
@@ -108,6 +112,7 @@ int PhoneInxFromKey(int key, int type, int kbtype, int searchTimes);
*/
size_t BopomofoFromUintArray(char *const bopomofo_buf, const size_t bopomofo_len, const uint16_t *phoneSeq);

+#ifndef WITH_RUST
/**
* @brief
* @param[out]
@@ -116,6 +121,7 @@ size_t BopomofoFromUintArray(char *const bopomofo_buf, const size_t bopomofo_len
* @return
*/
ssize_t UintArrayFromBopomofo(uint16_t *phone_seq, const size_t phone_len, const char *bopomofo_buf);
+#endif

/**
* @brief Get the length of the array of phones.
@@ -137,13 +143,14 @@ size_t GetPhoneLen(const uint16_t *phoneSeq);
*/
size_t GetBopomofoBufLen(size_t len);

-
+#ifndef WITH_RUST
/**
* @brief Get the length of phonetic symbols by its phonetic number.
* @param[in] phone_num phonetic number.
* @return the length of phone.
*/
size_t GetPhoneLenFromUint(uint16_t phone_num);
+#endif

/* *INDENT-OFF* */
#endif
diff --git a/src/common/key2pho.c b/src/common/key2pho.c
index 7f74264..bed6fd7 100644
--- a/src/common/key2pho.c
+++ b/src/common/key2pho.c
@@ -45,8 +45,11 @@ const char *const zhuin_tab[] = { /* number of bits */
};

static const int zhuin_tab_num[] = { 22, 4, 14, 5 };
+
+#ifndef WITH_RUST
static const int shift[] = { 9, 7, 3, 0 };
static const int mask[] = { 0x1F, 0x3, 0xF, 0x7 };
+#endif

static const char *const ph_str =
"\xE3\x84\x85\xE3\x84\x86\xE3\x84\x87\xE3\x84\x88"
@@ -91,6 +94,7 @@ static const char *const key_str[KB_TYPE_NUM] = {
*
* return the number it means. 0 means error.
*/
+#ifndef WITH_RUST
uint16_t UintFromPhone(const char *zhuin)
{
const char *iter;
@@ -122,6 +126,7 @@ uint16_t UintFromPhone(const char *zhuin)
}
return result;
}
+#endif

int PhoneFromKey(char *pho, const char *inputkey, int kbtype, int searchTimes)
{
@@ -153,6 +158,7 @@ int PhoneFromKey(char *pho, const char *inputkey, int kbtype, int searchTimes)
return 1;
}

+#ifndef WITH_RUST
int PhoneFromUint(char *phone, size_t phone_len, uint16_t phone_num)
{
int i;
@@ -181,6 +187,7 @@ int PhoneFromUint(char *phone, size_t phone_len, uint16_t phone_num)
return -1;
return 0;
}
+#endif

int PhoneInxFromKey(int key, int type, int kbtype, int searchTimes)
{
@@ -201,6 +208,7 @@ int PhoneInxFromKey(int key, int type, int kbtype, int searchTimes)
return zhuin_tab_num[type] - ueStrLen(p);
}

+#ifndef WITH_RUST
uint16_t UintFromPhoneInx(const int ph_inx[])
{
int i;
@@ -215,6 +223,7 @@ uint16_t UintFromPhoneInx(const int ph_inx[])

return result;
}
+#endif

size_t GetPhoneLen(const uint16_t *phoneSeq)
{
@@ -227,6 +236,7 @@ size_t GetPhoneLen(const uint16_t *phoneSeq)
return len;
}

+#ifndef WITH_RUST
static size_t GetBopomofoCount(const char *bopomofo_buf)
{
size_t count = 0;
@@ -240,6 +250,7 @@ static size_t GetBopomofoCount(const char *bopomofo_buf)

return count;
}
+#endif

size_t BopomofoFromUintArray(char *const bopomofo_buf, const size_t bopomofo_len, const uint16_t *phoneSeq)
{
@@ -265,6 +276,7 @@ size_t BopomofoFromUintArray(char *const bopomofo_buf, const size_t bopomofo_len
return buf_len;
}

+#ifndef WITH_RUST
ssize_t UintArrayFromBopomofo(uint16_t *phone_seq, const size_t phone_len, const char *bopomofo_buf)
{
size_t i;
@@ -289,12 +301,14 @@ ssize_t UintArrayFromBopomofo(uint16_t *phone_seq, const size_t phone_len, const

return len;
}
+#endif

size_t GetBopomofoBufLen(size_t len)
{
return (MAX_UTF8_SIZE * BOPOMOFO_SIZE + 1) * len;
}

+#ifndef WITH_RUST
size_t GetPhoneLenFromUint(uint16_t phone_num)
{
int i;
@@ -311,3 +325,4 @@ size_t GetPhoneLenFromUint(uint16_t phone_num)
}
return len > 0 ? (len + 1) : -1;
}
+#endif
diff --git a/src/lib.rs b/src/lib.rs
index e69de29..fbc5f1b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -0,0 +1 @@
+pub mod zhuyin;
\ No newline at end of file
diff --git a/src/zhuyin.rs b/src/zhuyin.rs
new file mode 100644
index 0000000..bcbfab5
--- /dev/null
+++ b/src/zhuyin.rs
@@ -0,0 +1,5 @@
+mod bopomofo;
+mod syllable;
+
+pub use bopomofo::{Bopomofo, BopomofoKind, ParseBopomofoError};
+pub use syllable::{DecodeSyllableError, IntoSyllablesBytes, Syllable, SyllableBuilder};
diff --git a/src/zhuyin/bopomofo.rs b/src/zhuyin/bopomofo.rs
new file mode 100644
index 0000000..8da997b
--- /dev/null
+++ b/src/zhuyin/bopomofo.rs
@@ -0,0 +1,298 @@
+use std::fmt::{Display, Write};
+
+use thiserror::Error;
+
+/// The category of the phonetic symbols
+///
+/// Zhuyin, or Bopomofo, consists of 37 letters and 4 tone marks. They are
+/// categorized into one of the four categories:
+///
+/// 1. Initial sounds: ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙ
+/// 2. Medial glides: ㄧㄨㄩ
+/// 3. Rimes: ㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦ
+/// 4. Tonal marks: ˙ˊˇˋ
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum BopomofoKind {
+ Initial = 0,
+ Medial,
+ Rime,
+ Tone,
+}
+
+/// Zhuyin Fuhao, often shortened as zhuyin and commonly called bopomofo
+///
+/// <https://simple.m.wikipedia.org/wiki/Zhuyin>
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Bopomofo {
+ /// ㄅ
+ B = 0,
+ /// ㄆ
+ P,
+ /// ㄇ
+ M,
+ /// ㄈ
+ F,
+ /// ㄉ
+ D,
+ /// ㄊ
+ T,
+ /// ㄋ
+ N,
+ /// ㄌ
+ L,
+ /// ㄍ
+ G,
+ /// ㄎ
+ K,
+ /// ㄏ
+ H,
+ /// ㄐ
+ J,
+ /// ㄑ
+ Q,
+ /// ㄒ
+ X,
+ /// ㄓ
+ ZH,
+ /// ㄔ
+ CH,
+ /// ㄕ
+ SH,
+ /// ㄖ
+ R,
+ /// ㄗ
+ Z,
+ /// ㄘ
+ C,
+ /// ㄙ
+ S,
+ /// 一
+ I,
+ /// ㄨ
+ U,
+ /// ㄩ
+ IU,
+ /// ㄚ
+ A,
+ /// ㄛ
+ O,
+ /// ㄜ
+ E,
+ /// ㄝ
+ EH,
+ /// ㄞ
+ AI,
+ /// ㄟ
+ EI,
+ /// ㄠ
+ AU,
+ /// ㄡ
+ OU,
+ /// ㄢ
+ AN,
+ /// ㄣ
+ EN,
+ /// ㄤ
+ ANG,
+ /// ㄥ
+ ENG,
+ /// ㄦ
+ ER,
+ /// ˙
+ TONE5,
+ /// ˊ
+ TONE2,
+ /// ˇ
+ TONE3,
+ /// ˋ
+ TONE4,
+ /// ˉ
+ TONE1,
+}
+
+use Bopomofo::*;
+
+const INITIAL_MAP: [Bopomofo; 21] = [
+ B, P, M, F, D, T, N, L, G, K, H, J, Q, X, ZH, CH, SH, R, Z, C, S,
+];
+const MEDIAL_MAP: [Bopomofo; 3] = [I, U, IU];
+const RIME_MAP: [Bopomofo; 13] = [A, O, E, EH, AI, EI, AU, OU, AN, EN, ANG, ENG, ER];
+const TONE_MAP: [Bopomofo; 4] = [TONE5, TONE2, TONE3, TONE4];
+
+impl Bopomofo {
+ pub const fn kind(&self) -> BopomofoKind {
+ match self {
+ B | P | M | F | D | T | N | L | G | K | H | J | Q | X | ZH | CH | SH | R | Z | C
+ | S => BopomofoKind::Initial,
+ I | U | IU => BopomofoKind::Medial,
+ A | O | E | EH | AI | EI | AU | OU | AN | EN | ANG | ENG | ER => BopomofoKind::Rime,
+ TONE1 | TONE2 | TONE3 | TONE4 | TONE5 => BopomofoKind::Tone,
+ }
+ }
+ pub const fn from_initial(index: u16) -> Result<Bopomofo, ParseBopomofoError> {
+ if index < 1 || (index - 1) as usize >= INITIAL_MAP.len() {
+ return Err(ParseBopomofoError {
+ kind: ParseBopomofoErrorKind::IndexOutOfRange,
+ });
+ }
+ Ok(INITIAL_MAP[(index - 1) as usize])
+ }
+ pub const fn from_medial(index: u16) -> Result<Bopomofo, ParseBopomofoError> {
+ if index < 1 || (index - 1) as usize >= MEDIAL_MAP.len() {
+ return Err(ParseBopomofoError {
+ kind: ParseBopomofoErrorKind::IndexOutOfRange,
+ });
+ }
+ Ok(MEDIAL_MAP[(index - 1) as usize])
+ }
+ pub const fn from_rime(index: u16) -> Result<Bopomofo, ParseBopomofoError> {
+ if index < 1 || (index - 1) as usize >= RIME_MAP.len() {
+ return Err(ParseBopomofoError {
+ kind: ParseBopomofoErrorKind::IndexOutOfRange,
+ });
+ }
+ Ok(RIME_MAP[(index - 1) as usize])
+ }
+ pub const fn from_tone(index: u16) -> Result<Bopomofo, ParseBopomofoError> {
+ if index < 1 || (index - 1) as usize >= TONE_MAP.len() {
+ return Err(ParseBopomofoError {
+ kind: ParseBopomofoErrorKind::IndexOutOfRange,
+ });
+ }
+ Ok(TONE_MAP[(index - 1) as usize])
+ }
+
+ pub fn initial_index(&self) -> u16 {
+ (INITIAL_MAP.iter().position(|b| b == self).unwrap() + 1) as u16
+ }
+ pub fn medial_index(&self) -> u16 {
+ (MEDIAL_MAP.iter().position(|b| b == self).unwrap() + 1) as u16
+ }
+ pub fn rime_index(&self) -> u16 {
+ (RIME_MAP.iter().position(|b| b == self).unwrap() + 1) as u16
+ }
+ pub fn tone_index(&self) -> u16 {
+ (TONE_MAP.iter().position(|b| b == self).unwrap() + 1) as u16
+ }
+}
+
+#[derive(Error, Debug)]
+#[error("parse bopomofo error: {:?}", kind)]
+pub struct ParseBopomofoError {
+ pub kind: ParseBopomofoErrorKind,
+}
+
+#[derive(Debug)]
+pub enum ParseBopomofoErrorKind {
+ UnknownSymbol,
+ IndexOutOfRange,
+}
+
+impl Display for Bopomofo {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_char((*self).into())
+ }
+}
+
+impl From<Bopomofo> for char {
+ fn from(bopomofo: Bopomofo) -> Self {
+ match bopomofo {
+ Bopomofo::B => 'ㄅ',
+ Bopomofo::P => 'ㄆ',
+ Bopomofo::M => 'ㄇ',
+ Bopomofo::F => 'ㄈ',
+ Bopomofo::D => 'ㄉ',
+ Bopomofo::T => 'ㄊ',
+ Bopomofo::N => 'ㄋ',
+ Bopomofo::L => 'ㄌ',
+ Bopomofo::G => 'ㄍ',
+ Bopomofo::K => 'ㄎ',
+ Bopomofo::H => 'ㄏ',
+ Bopomofo::J => 'ㄐ',
+ Bopomofo::Q => 'ㄑ',
+ Bopomofo::X => 'ㄒ',
+ Bopomofo::ZH => 'ㄓ',
+ Bopomofo::CH => 'ㄔ',
+ Bopomofo::SH => 'ㄕ',
+ Bopomofo::R => 'ㄖ',
+ Bopomofo::Z => 'ㄗ',
+ Bopomofo::C => 'ㄘ',
+ Bopomofo::S => 'ㄙ',
+ Bopomofo::A => 'ㄚ',
+ Bopomofo::O => 'ㄛ',
+ Bopomofo::E => 'ㄜ',
+ Bopomofo::EH => 'ㄝ',
+ Bopomofo::AI => 'ㄞ',
+ Bopomofo::EI => 'ㄟ',
+ Bopomofo::AU => 'ㄠ',
+ Bopomofo::OU => 'ㄡ',
+ Bopomofo::AN => 'ㄢ',
+ Bopomofo::EN => 'ㄣ',
+ Bopomofo::ANG => 'ㄤ',
+ Bopomofo::ENG => 'ㄥ',
+ Bopomofo::ER => 'ㄦ',
+ Bopomofo::I => 'ㄧ',
+ Bopomofo::U => 'ㄨ',
+ Bopomofo::IU => 'ㄩ',
+ Bopomofo::TONE1 => 'ˉ',
+ Bopomofo::TONE5 => '˙',
+ Bopomofo::TONE2 => 'ˊ',
+ Bopomofo::TONE3 => 'ˇ',
+ Bopomofo::TONE4 => 'ˋ',
+ }
+ }
+}
+
+impl TryFrom<char> for Bopomofo {
+ type Error = ParseBopomofoError;
+
+ fn try_from(c: char) -> Result<Bopomofo, ParseBopomofoError> {
+ match c {
+ 'ㄅ' => Ok(Bopomofo::B),
+ 'ㄆ' => Ok(Bopomofo::P),
+ 'ㄇ' => Ok(Bopomofo::M),
+ 'ㄈ' => Ok(Bopomofo::F),
+ 'ㄉ' => Ok(Bopomofo::D),
+ 'ㄊ' => Ok(Bopomofo::T),
+ 'ㄋ' => Ok(Bopomofo::N),
+ 'ㄌ' => Ok(Bopomofo::L),
+ 'ㄍ' => Ok(Bopomofo::G),
+ 'ㄎ' => Ok(Bopomofo::K),
+ 'ㄏ' => Ok(Bopomofo::H),
+ 'ㄐ' => Ok(Bopomofo::J),
+ 'ㄑ' => Ok(Bopomofo::Q),
+ 'ㄒ' => Ok(Bopomofo::X),
+ 'ㄓ' => Ok(Bopomofo::ZH),
+ 'ㄔ' => Ok(Bopomofo::CH),
+ 'ㄕ' => Ok(Bopomofo::SH),
+ 'ㄖ' => Ok(Bopomofo::R),
+ 'ㄗ' => Ok(Bopomofo::Z),
+ 'ㄘ' => Ok(Bopomofo::C),
+ 'ㄙ' => Ok(Bopomofo::S),
+ 'ㄚ' => Ok(Bopomofo::A),
+ 'ㄛ' => Ok(Bopomofo::O),
+ 'ㄜ' => Ok(Bopomofo::E),
+ 'ㄝ' => Ok(Bopomofo::EH),
+ 'ㄞ' => Ok(Bopomofo::AI),
+ 'ㄟ' => Ok(Bopomofo::EI),
+ 'ㄠ' => Ok(Bopomofo::AU),
+ 'ㄡ' => Ok(Bopomofo::OU),
+ 'ㄢ' => Ok(Bopomofo::AN),
+ 'ㄣ' => Ok(Bopomofo::EN),
+ 'ㄤ' => Ok(Bopomofo::ANG),
+ 'ㄥ' => Ok(Bopomofo::ENG),
+ 'ㄦ' => Ok(Bopomofo::ER),
+ 'ㄧ' => Ok(Bopomofo::I),
+ 'ㄨ' => Ok(Bopomofo::U),
+ 'ㄩ' => Ok(Bopomofo::IU),
+ 'ˉ' => Ok(Bopomofo::TONE1),
+ '˙' => Ok(Bopomofo::TONE5),
+ 'ˊ' => Ok(Bopomofo::TONE2),
+ 'ˇ' => Ok(Bopomofo::TONE3),
+ 'ˋ' => Ok(Bopomofo::TONE4),
+ _ => Err(ParseBopomofoError {
+ kind: ParseBopomofoErrorKind::UnknownSymbol,
+ }),
+ }
+ }
+}
diff --git a/src/zhuyin/syllable.rs b/src/zhuyin/syllable.rs
new file mode 100644
index 0000000..f58fced
--- /dev/null
+++ b/src/zhuyin/syllable.rs
@@ -0,0 +1,485 @@
+use std::{
+ fmt::{Display, Write},
+ str::FromStr,
+};
+
+use thiserror::Error;
+
+use super::{Bopomofo, BopomofoKind, ParseBopomofoError};
+
+/// The consonants and vowels that are taken together to make a single sound.
+///
+/// <https://en.m.wikipedia.org/wiki/Syllable#Chinese_model>
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct Syllable {
+ value: u16,
+}
+
+impl Syllable {
+ pub const fn new() -> Syllable {
+ Syllable { value: 0 }
+ }
+
+ pub const fn builder() -> SyllableBuilder {
+ SyllableBuilder::new()
+ }
+ pub const fn initial(&self) -> Option<Bopomofo> {
+ let index = self.value >> 9;
+ if index == 0 {
+ None
+ } else {
+ match Bopomofo::from_initial(index) {
+ Ok(v) => Some(v),
+ Err(_) => panic!(),
+ }
+ }
+ }
+ #[allow(clippy::unusual_byte_groupings)]
+ pub const fn medial(&self) -> Option<Bopomofo> {
+ let index = (self.value & 0b0000000_11_0000_000) >> 7;
+ if index == 0 {
+ None
+ } else {
+ match Bopomofo::from_medial(index) {
+ Ok(v) => Some(v),
+ Err(_) => panic!(),
+ }
+ }
+ }
+ #[allow(clippy::unusual_byte_groupings)]
+ pub const fn rime(&self) -> Option<Bopomofo> {
+ let index = (self.value & 0b0000000_00_1111_000) >> 3;
+ if index == 0 {
+ None
+ } else {
+ match Bopomofo::from_rime(index) {
+ Ok(v) => Some(v),
+ Err(_) => panic!(),
+ }
+ }
+ }
+ #[allow(clippy::unusual_byte_groupings)]
+ pub const fn tone(&self) -> Option<Bopomofo> {
+ let index = self.value & 0b0000000_00_0000_111;
+ if index == 0 {
+ None
+ } else {
+ match Bopomofo::from_tone(index) {
+ Ok(v) => Some(v),
+ Err(_) => panic!(),
+ }
+ }
+ }
+ pub fn remove_initial(&mut self) -> Option<Bopomofo> {
+ let ret = self.initial();
+ self.value &= 0b0000_0001_1111_1111;
+ ret
+ }
+ pub fn remove_medial(&mut self) -> Option<Bopomofo> {
+ let ret = self.medial();
+ self.value &= 0b1111_1110_0111_1111;
+ ret
+ }
+ pub fn remove_rime(&mut self) -> Option<Bopomofo> {
+ let ret = self.rime();
+ self.value &= 0b1111_1111_1000_0111;
+ ret
+ }
+ pub fn remove_tone(&mut self) -> Option<Bopomofo> {
+ let ret = self.tone();
+ self.value &= 0b1111_1111_1111_1000;
+ ret
+ }
+ pub fn is_empty(&self) -> bool {
+ self.value == 0
+ }
+ pub fn has_initial(&self) -> bool {
+ self.initial().is_some()
+ }
+ pub fn has_medial(&self) -> bool {
+ self.medial().is_some()
+ }
+ pub fn has_rime(&self) -> bool {
+ self.rime().is_some()
+ }
+ pub fn has_tone(&self) -> bool {
+ self.tone().is_some()
+ }
+ /// Returns the `Syllable` encoded in a u16 integer.
+ ///
+ /// The data layout used:
+ ///
+ /// ```text
+ /// 0 1
+ /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ /// | Initial | M | Rime |Tone |
+ /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ /// ```
+ pub fn to_u16(&self) -> u16 {
+ debug_assert!(
+ !self.is_empty(),
+ "empty syllable cannot be converted to u16"
+ );
+ self.value
+ }
+ /// Returns the `Syllable` encoded in a u16 integer in little-endian bytes.
+ ///
+ /// The data layout used:
+ ///
+ /// ```text
+ /// 0 1
+ /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ /// | Initial | M | Rime |Tone |
+ /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ /// ```
+ pub fn to_le_bytes(&self) -> [u8; 2] {
+ self.to_u16().to_le_bytes()
+ }
+ pub fn update(&mut self, bopomofo: Bopomofo) {
+ match bopomofo.kind() {
+ BopomofoKind::Initial => {
+ self.remove_initial();
+ self.value |= (bopomofo as u16 + 1) << 9;
+ }
+ BopomofoKind::Medial => {
+ self.remove_medial();
+ self.value |= (bopomofo as u16 - 20) << 7;
+ }
+ BopomofoKind::Rime => {
+ self.remove_rime();
+ self.value |= (bopomofo as u16 - 23) << 3;
+ }
+ BopomofoKind::Tone => {
+ self.remove_tone();
+ self.value |= bopomofo as u16 - 36;
+ }
+ };
+ }
+ pub fn pop(&mut self) -> Option<Bopomofo> {
+ if self.tone().is_some() {
+ return self.remove_tone();
+ }
+ if self.rime().is_some() {
+ return self.remove_rime();
+ }
+ if self.medial().is_some() {
+ return self.remove_medial();
+ }
+ if self.initial().is_some() {
+ return self.remove_initial();
+ }
+ None
+ }
+ pub fn clear(&mut self) {
+ *self = Syllable::new()
+ }
+}
+
+impl Default for Syllable {
+ fn default() -> Self {
+ Syllable::new()
+ }
+}
+
+impl From<Syllable> for u16 {
+ fn from(syl: Syllable) -> Self {
+ syl.to_u16()
+ }
+}
+
+impl From<&Syllable> for u16 {
+ fn from(syl: &Syllable) -> Self {
+ syl.to_u16()
+ }
+}
+
+impl TryFrom<u16> for Syllable {
+ type Error = DecodeSyllableError;
+
+ #[allow(clippy::unusual_byte_groupings)]
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ // TODO check invalid value
+ Ok(Syllable { value })
+ }
+}
+
+impl FromStr for Syllable {
+ type Err = ParseSyllableError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let mut builder = Syllable::builder();
+ for c in s.chars() {
+ let bopomofo = Bopomofo::try_from(c)?;
+ builder = builder.insert(bopomofo)?;
+ }
+ Ok(builder.build())
+ }
+}
+
+pub trait IntoSyllablesBytes {
+ fn into_syllables_bytes(self) -> Vec<u8>;
+}
+
+impl<T> IntoSyllablesBytes for T
+where
+ T: IntoIterator,
+ T::Item: Into<u16>,
+{
+ fn into_syllables_bytes(self) -> Vec<u8> {
+ let mut syllables_bytes = vec![];
+ self.into_iter()
+ .for_each(|syl| syllables_bytes.extend_from_slice(&syl.into().to_le_bytes()));
+ syllables_bytes
+ }
+}
+
+impl Display for Syllable {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ for &bopomofo in [&self.initial(), &self.medial(), &self.rime(), &self.tone()] {
+ if let Some(bopomofo) = bopomofo {
+ f.write_char(bopomofo.into())?;
+ }
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug)]
+pub struct SyllableBuilder {
+ value: u16,
+ step: u8,
+}
+
+impl Default for SyllableBuilder {
+ fn default() -> SyllableBuilder {
+ SyllableBuilder::new()
+ }
+}
+
+impl SyllableBuilder {
+ pub const fn new() -> SyllableBuilder {
+ SyllableBuilder { value: 0, step: 0 }
+ }
+ pub const fn insert(
+ mut self,
+ bopomofo: Bopomofo,
+ ) -> Result<SyllableBuilder, BuildSyllableError> {
+ match bopomofo.kind() {
+ BopomofoKind::Initial => {
+ if self.step > 0 {
+ return Err(BuildSyllableError {
+ msg: "bopomofo is in incorrect order",
+ });
+ }
+ if self.value & 0b1111_1110_0000_0000 != 0 {
+ return Err(BuildSyllableError {
+ msg: "multiple initial bopomofo",
+ });
+ }
+ self.step = 1;
+ self.value |= (bopomofo as u16 + 1) << 9;
+ }
+ BopomofoKind::Medial => {
+ if self.step > 1 {
+ return Err(BuildSyllableError {
+ msg: "bopomofo is in incorrect order",
+ });
+ }
+ if self.value & 0b0000_0001_1000_0000 != 0 {
+ return Err(BuildSyllableError {
+ msg: "multiple medial bopomofo",
+ });
+ }
+ self.step = 2;
+ self.value |= (bopomofo as u16 - 20) << 7;
+ }
+ BopomofoKind::Rime => {
+ if self.step > 2 {
+ return Err(BuildSyllableError {
+ msg: "bopomofo is in incorrect order",
+ });
+ }
+ if self.value & 0b0000_0000_0111_1000 != 0 {
+ return Err(BuildSyllableError {
+ msg: "multiple rime bopomofo",
+ });
+ }
+ self.step = 3;
+ self.value |= (bopomofo as u16 - 23) << 3;
+ }
+ BopomofoKind::Tone => {
+ if self.step > 3 {
+ return Err(BuildSyllableError {
+ msg: "bopomofo is in incorrect order",
+ });
+ }
+ if self.value & 0b0000_0000_0000_0111 != 0 {
+ return Err(BuildSyllableError {
+ msg: "multiple tone bopomofo",
+ });
+ }
+ self.step = 4;
+ self.value |= bopomofo as u16 - 36;
+ }
+ };
+ Ok(self)
+ }
+ pub const fn build(self) -> Syllable {
+ Syllable { value: self.value }
+ }
+}
+
+#[derive(Error, Debug)]
+#[error("syllable decode error: {msg}")]
+pub struct DecodeSyllableError {
+ msg: String,
+ source: Box<dyn std::error::Error>,
+}
+
+#[derive(Error, Debug)]
+#[error("syllable build error: {msg}")]
+pub struct BuildSyllableError {
+ msg: &'static str,
+}
+
+#[derive(Error, Debug)]
+#[error("syllable parse error")]
+pub struct ParseSyllableError {
+ source: Box<dyn std::error::Error>,
+}
+
+impl From<ParseBopomofoError> for ParseSyllableError {
+ fn from(value: ParseBopomofoError) -> Self {
+ ParseSyllableError {
+ source: value.into(),
+ }
+ }
+}
+
+impl From<BuildSyllableError> for ParseSyllableError {
+ fn from(value: BuildSyllableError) -> Self {
+ ParseSyllableError {
+ source: value.into(),
+ }
+ }
+}
+
+/// Build a syllable from bopomofos
+///
+/// # Examples
+///
+/// Build a syllable
+/// ```
+/// use chewing::zhuyin::Bopomofo::{K, U, TONE4};
+/// use chewing::syl;
+///
+/// let syl = syl![K, U, TONE4];
+///
+/// assert_eq!("ㄎㄨˋ", syl.to_string());
+/// ```
+#[macro_export]
+macro_rules! syl {
+ () => { $crate::zhuyin::Syllable::new() };
+ ($($bopomofo:expr),+) => {
+ {
+ let mut builder = $crate::zhuyin::Syllable::builder();
+ $(builder = match builder.insert($bopomofo) {
+ Ok(b) => b,
+ Err(_) => panic!("unable to build syllable"),
+ };)+
+ builder.build()
+ }
+ };
+}
+
+#[cfg(test)]
+mod test {
+
+ use super::{Bopomofo, Syllable};
+
+ #[test]
+ fn syllable_hsu_sdf_as_u16() {
+ let syl = Syllable::builder().insert(Bopomofo::S).unwrap().build();
+ assert_eq!(0x2A00, syl.to_u16());
+
+ let syl = Syllable::builder().insert(Bopomofo::D).unwrap().build();
+ assert_eq!(0xA00, syl.to_u16());
+
+ let syl = Syllable::builder().insert(Bopomofo::F).unwrap().build();
+ assert_eq!(0x800, syl.to_u16());
+ }
+
+ #[test]
+ #[should_panic]
+ fn empty_syllable_as_u16() {
+ Syllable::builder().build().to_u16();
+ }
+
+ #[test]
+ fn syllable_as_u16_roundtrip() {
+ let syl = Syllable::builder().insert(Bopomofo::S).unwrap().build();
+ assert_eq!(syl, syl.to_u16().try_into().unwrap());
+ }
+
+ #[test]
+ fn syl_macro_rules() {
+ let syl = syl![];
+ assert_eq!(Syllable::new(), syl);
+
+ let syl = syl![Bopomofo::S];
+ assert_eq!(
+ Syllable::builder().insert(Bopomofo::S).unwrap().build(),
+ syl
+ );
+
+ let syl = syl![Bopomofo::S, Bopomofo::I, Bopomofo::EN, Bopomofo::TONE4];
+ assert_eq!(
+ Syllable::builder()
+ .insert(Bopomofo::S)
+ .unwrap()
+ .insert(Bopomofo::I)
+ .unwrap()
+ .insert(Bopomofo::EN)
+ .unwrap()
+ .insert(Bopomofo::TONE4)
+ .unwrap()
+ .build(),
+ syl
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ fn syl_macro_rules_fool_proof() {
+ syl![Bopomofo::S, Bopomofo::D];
+ }
+
+ #[test]
+ fn syl_macro_rules_comiles_in_const() {
+ const SYLLABLE: Syllable = syl![Bopomofo::S, Bopomofo::I, Bopomofo::EN];
+ assert_eq!(
+ Syllable::builder()
+ .insert(Bopomofo::S)
+ .unwrap()
+ .insert(Bopomofo::I)
+ .unwrap()
+ .insert(Bopomofo::EN)
+ .unwrap()
+ .build(),
+ SYLLABLE
+ );
+ }
+
+ #[test]
+ fn new_and_pop_bopomofo() {
+ let mut syl = syl![Bopomofo::S, Bopomofo::I, Bopomofo::EN, Bopomofo::TONE4];
+ assert_eq!(Some(Bopomofo::TONE4), syl.pop());
+ assert_eq!(Some(Bopomofo::EN), syl.pop());
+ assert_eq!(Some(Bopomofo::I), syl.pop());
+ assert_eq!(Some(Bopomofo::S), syl.pop());
+ assert_eq!(None, syl.pop());
+ assert_eq!(syl![], syl);
+ }
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 627525b..821bf58 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -48,6 +48,9 @@ endif()

foreach(target ${ALL_TESTS})
add_executable(${target} ${TEST_SRC_DIR}/${target}.c)
+ if (WITH_RUST)
+ target_link_libraries(${target} chewing-internal)
+ endif()
add_dependencies(${target} data all_static_data)
add_dependencies(check ${target})
endforeach()
--
2.39.1

Kan-Ru Chen

unread,
Jan 26, 2023, 8:43:02 PM1/26/23
to chewin...@googlegroups.com, czc...@czchen.org, jser...@gmail.com, Kan-Ru Chen
---
CMakeLists.txt | 35 +-
Cargo.lock | 398 +-
Cargo.toml | 11 +-
capi/chewing-internal/Cargo.toml | 4 +-
capi/chewing-internal/cbindgen.toml | 4 +-
.../include/chewing_internal.h | 472 +
capi/chewing-internal/src/binding.rs | 13 +
capi/chewing-internal/src/bopomofo.rs | 242 +
capi/chewing-internal/src/compat.rs | 11 +
capi/chewing-internal/src/conversion.rs | 94 +
capi/chewing-internal/src/dict.rs | 165 +
capi/chewing-internal/src/ffi.rs | 21 +
capi/chewing-internal/src/key2pho.rs | 16 +-
capi/chewing-internal/src/lib.rs | 12 +-
capi/chewing-internal/src/path.rs | 86 +
capi/chewing-internal/src/types.rs | 256 +
capi/chewing-internal/src/userphrase.rs | 299 +
capi/chewing-internal/src/utf8.rs | 94 +
capi/chewing-public/Cargo.toml | 2 +-
capi/chewing-public/src/lib.rs | 2 +-
data/CMakeLists.txt | 24 +-
data/word.src | 26096 ++++++++++++++++
include/internal/bopomofo-private.h | 4 +
include/internal/chewing-private.h | 32 +-
src/bopomofo.c | 21 +
src/chewing-sql.c | 82 +-
src/chewing.c | 0
src/chewingio.c | 222 +-
src/chewingutil.c | 165 +-
src/choice.c | 42 +-
src/conversion.rs | 38 +
src/conversion/chewing_conversion.rs | 698 +
src/dict.c | 20 +-
src/dictionary.rs | 445 +
src/dictionary/layered.rs | 188 +
src/dictionary/sqlite.rs | 625 +
src/dictionary/trie.rs | 1224 +
src/editor.rs | 6 +
src/editor/estimate.rs | 112 +
src/editor/keymap.rs | 159 +
src/editor/layout.rs | 87 +
src/editor/layout/dc26.rs | 185 +
src/editor/layout/et.rs | 135 +
src/editor/layout/et26.rs | 222 +
src/editor/layout/ginyieh.rs | 134 +
src/editor/layout/hsu.rs | 281 +
src/editor/layout/ibm.rs | 135 +
src/editor/layout/pinyin.rs | 498 +
src/editor/layout/standard.rs | 136 +
src/hash.c | 54 +-
src/lib.rs | 10 +-
src/mod_aux.c | 8 +-
src/path.rs | 58 +
src/pinyin.c | 42 +-
src/private.h | 37 +
src/tree.c | 26 +-
src/userphrase-hash.c | 8 +-
src/userphrase-sql.c | 76 +-
test/CMakeLists.txt | 13 +-
test/test-bopomofo.c | 5 +
test/test-path.c | 11 +
test/testhelper.c | 8 +-
test/testhelper.h | 10 +-
tools/Cargo.toml | 12 +
tools/src/bin/init_database.rs | 121 +
65 files changed, 34410 insertions(+), 342 deletions(-)
create mode 100644 capi/chewing-internal/src/binding.rs
create mode 100644 capi/chewing-internal/src/bopomofo.rs
create mode 100644 capi/chewing-internal/src/compat.rs
create mode 100644 capi/chewing-internal/src/conversion.rs
create mode 100644 capi/chewing-internal/src/dict.rs
create mode 100644 capi/chewing-internal/src/ffi.rs
create mode 100644 capi/chewing-internal/src/path.rs
create mode 100644 capi/chewing-internal/src/types.rs
create mode 100644 capi/chewing-internal/src/userphrase.rs
create mode 100644 capi/chewing-internal/src/utf8.rs
create mode 100644 data/word.src
create mode 100644 src/chewing.c
create mode 100644 src/conversion.rs
create mode 100644 src/conversion/chewing_conversion.rs
create mode 100644 src/dictionary.rs
create mode 100644 src/dictionary/layered.rs
create mode 100644 src/dictionary/sqlite.rs
create mode 100644 src/dictionary/trie.rs
create mode 100644 src/editor.rs
create mode 100644 src/editor/estimate.rs
create mode 100644 src/editor/keymap.rs
create mode 100644 src/editor/layout.rs
create mode 100644 src/editor/layout/dc26.rs
create mode 100644 src/editor/layout/et.rs
create mode 100644 src/editor/layout/et26.rs
create mode 100644 src/editor/layout/ginyieh.rs
create mode 100644 src/editor/layout/hsu.rs
create mode 100644 src/editor/layout/ibm.rs
create mode 100644 src/editor/layout/pinyin.rs
create mode 100644 src/editor/layout/standard.rs
create mode 100644 src/path.rs
create mode 100644 tools/Cargo.toml
create mode 100644 tools/src/bin/init_database.rs

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 76c155f..90ab4f5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,7 +75,7 @@ option(USE_VALGRIND "Use valgrind when testing" true)
option(WITH_RUST "Use rust implemented internals (experimental)" true)
if (WITH_RUST)
add_subdirectory(cmake/corrosion)
- corrosion_import_crate(MANIFEST_PATH Cargo.toml)
+ corrosion_import_crate(MANIFEST_PATH Cargo.toml NO_LINKER_OVERRIDE)
add_compile_definitions(WITH_RUST)
endif()

@@ -167,8 +167,8 @@ set(ALL_INC
)

if (WITH_RUST)
- include_directories(capi/chewing-internal/include)
include_directories(capi/chewing-public/include)
+ include_directories(capi/chewing-internal/include)
list(APPEND ALL_INC capi/chewing-public/include/chewing_rs.h)
endif()

@@ -182,8 +182,7 @@ add_library(common OBJECT
${INC_DIR}/internal/key2pho-private.h
${INC_DIR}/internal/memory-private.h

- ${SRC_DIR}/common/chewing-utf8-util.c
- ${SRC_DIR}/common/key2pho.c
+ src/chewing.c
)

add_library(chewing OBJECT
@@ -191,19 +190,28 @@ add_library(chewing OBJECT
${INC_DIR}/internal/chewing-private.h
${INC_DIR}/internal/chewingutil.h
${INC_DIR}/internal/choice-private.h
- ${INC_DIR}/internal/dict-private.h
${INC_DIR}/internal/global-private.h
- ${INC_DIR}/internal/pinyin-private.h
- ${INC_DIR}/internal/tree-private.h
- ${INC_DIR}/internal/userphrase-private.h
- ${INC_DIR}/internal/bopomofo-private.h

${SRC_DIR}/compat.c
${SRC_DIR}/chewingio.c
${SRC_DIR}/chewingutil.c
${SRC_DIR}/choice.c
- ${SRC_DIR}/dict.c
${SRC_DIR}/mod_aux.c
+ ${SRC_DIR}/private.h
+)
+if (NOT WITH_RUST)
+target_sources(common PRIVATE
+ ${SRC_DIR}/common/chewing-utf8-util.c
+ ${SRC_DIR}/common/key2pho.c
+)
+target_sources(chewing PRIVATE
+ ${INC_DIR}/internal/dict-private.h
+ ${INC_DIR}/internal/pinyin-private.h
+ ${INC_DIR}/internal/tree-private.h
+ ${INC_DIR}/internal/userphrase-private.h
+ ${INC_DIR}/internal/bopomofo-private.h
+
+ ${SRC_DIR}/dict.c
${SRC_DIR}/pinyin.c
${SRC_DIR}/porting_layer/include/plat_mmap.h
${SRC_DIR}/porting_layer/include/plat_path.h
@@ -214,15 +222,16 @@ add_library(chewing OBJECT
${SRC_DIR}/porting_layer/src/plat_mmap_windows.c
${SRC_DIR}/porting_layer/src/plat_path.c
${SRC_DIR}/porting_layer/src/rpl_malloc.c
- ${SRC_DIR}/private.h
${SRC_DIR}/tree.c
${SRC_DIR}/userphrase.c
${SRC_DIR}/bopomofo.c
)
+endif()
target_compile_definitions(chewing PRIVATE
CHEWING_DATADIR=\"${CMAKE_INSTALL_FULL_DATADIR}/libchewing\"
)

+if (NOT WITH_RUST)
if (WITH_SQLITE3)
add_library(userphrase STATIC
${INC_DIR}/internal/chewing-sql.h
@@ -262,6 +271,7 @@ else()
${SRC_DIR}/userphrase-hash.c
)
endif()
+endif()

if (BUILD_DLL OR NOT MSVC)
if (MSVC)
@@ -285,13 +295,14 @@ if (NOT BUILD_DLL)
endif()

foreach(lib ${LIBS})
- target_link_libraries(${lib} userphrase)
if (WITH_SQLITE3 AND NOT WITH_INTERNAL_SQLITE3)
target_link_libraries(${lib} ${SQLITE3_LIBRARY})
endif()
if (WITH_RUST)
target_link_libraries(${lib} chewing-public)
target_link_libraries(${lib} chewing-internal)
+ else()
+ target_link_libraries(${lib} userphrase)
endif()
endforeach()

diff --git a/Cargo.lock b/Cargo.lock
index 49659ae..3574fae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,13 +2,30 @@
# It is not intended for manual editing.
version = 3

+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
+
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
"libc",
"winapi",
]
@@ -19,6 +36,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"

+[[package]]
+name = "binary-layout"
+version = "3.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eb8cda7e0cb274442a8ac32a9d5719e89355a4871be9dccae681fb8f5e9ec1c"
+dependencies = [
+ "doc-comment",
+ "paste",
+]
+
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -31,7 +58,7 @@ version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb"
dependencies = [
- "clap",
+ "clap 3.2.23",
"heck",
"indexmap",
"log",
@@ -44,6 +71,12 @@ dependencies = [
"toml",
]

+[[package]]
+name = "cc"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -54,7 +87,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "chewing"
version = "0.5.1-alpha.1"
dependencies = [
+ "binary-layout",
+ "dirs-next",
+ "indexmap",
+ "riff",
+ "rusqlite",
+ "tempfile",
"thiserror",
+ "tracing",
]

[[package]]
@@ -63,6 +103,8 @@ version = "0.5.1-alpha.1"
dependencies = [
"cbindgen",
"chewing",
+ "chewing-public",
+ "ffi-opaque",
]

[[package]]
@@ -73,6 +115,16 @@ dependencies = [
"chewing",
]

+[[package]]
+name = "chewing-tools"
+version = "0.5.1-alpha.1"
+dependencies = [
+ "anyhow",
+ "chewing",
+ "clap 4.1.4",
+ "thiserror",
+]
+
[[package]]
name = "clap"
version = "3.2.23"
@@ -81,13 +133,26 @@ checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"atty",
"bitflags",
- "clap_lex",
+ "clap_lex 0.2.4",
"indexmap",
"strsim",
"termcolor",
"textwrap",
]

+[[package]]
+name = "clap"
+version = "4.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
+dependencies = [
+ "bitflags",
+ "clap_lex 0.3.1",
+ "is-terminal",
+ "strsim",
+ "termcolor",
+]
+
[[package]]
name = "clap_lex"
version = "0.2.4"
@@ -97,6 +162,75 @@ dependencies = [
"os_str_bytes",
]

+[[package]]
+name = "clap_lex"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
[[package]]
name = "fastrand"
version = "1.8.0"
@@ -106,11 +240,40 @@ dependencies = [
"instant",
]

+[[package]]
+name = "ffi-opaque"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec54ac60a7f2ee9a97cad9946f9bf629a3bc6a7ae59e68983dc9318f5a54b81a"
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa"
+dependencies = [
+ "hashbrown",
+]

[[package]]
name = "heck"
@@ -127,6 +290,15 @@ dependencies = [
"libc",
]

+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "indexmap"
version = "1.9.2"
@@ -146,6 +318,28 @@ dependencies = [
"cfg-if",
]

+[[package]]
+name = "io-lifetimes"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys",
+]
+
[[package]]
name = "itoa"
version = "1.0.5"
@@ -158,6 +352,22 @@ version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"

+[[package]]
+name = "libsqlite3-sys"
+version = "0.25.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
[[package]]
name = "log"
version = "0.4.17"
@@ -167,12 +377,36 @@ dependencies = [
"cfg-if",
]

+[[package]]
+name = "once_cell"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
+
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"

+[[package]]
+name = "paste"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
[[package]]
name = "proc-macro2"
version = "1.0.50"
@@ -200,6 +434,17 @@ dependencies = [
"bitflags",
]

+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+ "thiserror",
+]
+
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@@ -209,6 +454,40 @@ dependencies = [
"winapi",
]

+[[package]]
+name = "riff"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1"
+
+[[package]]
+name = "rusqlite"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
+name = "rustix"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
[[package]]
name = "ryu"
version = "1.0.12"
@@ -246,6 +525,12 @@ dependencies = [
"serde",
]

+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
[[package]]
name = "strsim"
version = "0.10.0"
@@ -321,12 +606,62 @@ dependencies = [
"serde",
]

+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
+dependencies = [
+ "once_cell",
+]
+
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"

+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -357,3 +692,60 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
diff --git a/Cargo.toml b/Cargo.toml
index 4db5cf4..7be2878 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,12 +7,19 @@ version = "0.5.1-alpha.1"
edition = "2021"

[dependencies]
+binary-layout = "3.0.0"
+dirs-next = "2.0.0"
+indexmap = "1.9.2"
+riff = "1.0.0"
+rusqlite = "0.28.0"
thiserror = "1.0.0"
+tracing = "0.1.37"

[dev-dependencies]
+tempfile = "3"

[workspace]
-members = ["capi/chewing-internal", "capi/chewing-public"]
+members = ["capi/chewing-internal", "capi/chewing-public", "tools"]

[profile.release]
-lto = "thin"
+lto = "thin"
\ No newline at end of file
diff --git a/capi/chewing-internal/Cargo.toml b/capi/chewing-internal/Cargo.toml
index dd53c9b..c5f1bed 100644
--- a/capi/chewing-internal/Cargo.toml
+++ b/capi/chewing-internal/Cargo.toml
@@ -7,9 +7,11 @@ edition = "2021"

[dependencies]
chewing = { version = "0.5.1-alpha.1", path = "../.." }
+chewing-public = { version = "0.5.1-alpha.1", path = "../chewing-public" }
+ffi-opaque = "2.0.0"

[lib]
-crate-type = ["staticlib"]
+crate-type = ["rlib", "staticlib"]

[build-dependencies]
cbindgen = "0.24.3"
diff --git a/capi/chewing-internal/cbindgen.toml b/capi/chewing-internal/cbindgen.toml
index af85092..afd1357 100644
--- a/capi/chewing-internal/cbindgen.toml
+++ b/capi/chewing-internal/cbindgen.toml
@@ -16,7 +16,7 @@ header = """
include_guard = "chewing_internal_bindings_h"
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
pragma_once = true
-include_version = false
+includes = ["chewing_rs.h"]
cpp_compat = true
usize_is_size_t = true

@@ -31,4 +31,4 @@ rename_fields = "CamelCase"
rename_variants = "ScreamingSnakeCase"

[export]
-include = []
+include = ["BOPOMOFO", "KB", "UserUpdate", "ChewingContext", "Phrase"]
diff --git a/capi/chewing-internal/include/chewing_internal.h b/capi/chewing-internal/include/chewing_internal.h
index 3d7bbbf..9d2a690 100644
--- a/capi/chewing-internal/include/chewing_internal.h
+++ b/capi/chewing-internal/include/chewing_internal.h
@@ -18,11 +18,422 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
+#include "chewing_rs.h"
+
+#define MAX_UTF8_SIZE 4
+
+#define MAX_UTF8_BUF (MAX_UTF8_SIZE + 1)
+
+#define BOPOMOFO_SIZE 4
+
+#define MAX_BOPOMOFO_UTF8_BUF ((BOPOMOFO_SIZE * MAX_UTF8_SIZE) + 1)
+
+#define PINYIN_SIZE 10
+
+#define MAX_PHRASE_LEN 11
+
+#define MAX_PHRASE_UTF8_BUF ((MAX_PHRASE_LEN * MAX_UTF8_SIZE) + 1)
+
+#define MAX_PHONE_SEQ_LEN 50
+
+#define MAX_PHONE_SEQ_BUF (MAX_PHONE_SEQ_LEN + 1)
+
+#define MAX_PHONE_SEQ_UTF8_BUF ((MAX_PHONE_SEQ_LEN * MAX_UTF8_SIZE) + 1)
+
+#define MIN_CHI_SYMBOL_LEN 0
+
+#define MAX_CHI_SYMBOL_LEN (MAX_PHONE_SEQ_LEN - MAX_PHRASE_LEN)
+
+#define MAX_INTERVAL (((MAX_PHONE_SEQ_LEN + 1) * MAX_PHONE_SEQ_LEN) / 2)
+
+#define MAX_CHOICE 567
+
+#define MAX_CHOICE_BUF 50
+
+#define EASY_SYMBOL_KEY_TAB_LEN 36
+
+#define AUX_PREFIX_LEN 3
+
+#define MAX_SHOW_MSG_BUF ((MAX_UTF8_SIZE * (MAX_PHRASE_LEN + AUX_PREFIX_LEN)) + 1)
+
+#define N_HASH_BIT 14
+
+#define HASH_TABLE_SIZE (1 << N_HASH_BIT)
+
+#define WORD_CHOICE 0
+
+#define SYMBOL_CATEGORY_CHOICE 1
+
+#define SYMBOL_CHOICE_INSERT 2
+
+#define SYMBOL_CHOICE_UPDATE 3
+
+typedef enum BOPOMOFO {
+ BOPOMOFO_IGNORE,
+ BOPOMOFO_ABSORB,
+ BOPOMOFO_COMMIT,
+ BOPOMOFO_KEY_ERROR,
+ BOPOMOFO_ERROR,
+ BOPOMOFO_NO_WORD,
+ BOPOMOFO_OPEN_SYMBOL_TABLE,
+} BOPOMOFO;
+
+typedef enum Category {
+ CHEWING_NONE,
+ CHEWING_CHINESE,
+ CHEWING_SYMBOL,
+} Category;
+
+typedef enum KB {
+ KB_DEFAULT,
+ KB_HSU,
+ KB_IBM,
+ KB_GIN_YIEH,
+ KB_ET,
+ KB_ET26,
+ KB_DVORAK,
+ KB_DVORAK_HSU,
+ KB_DACHEN_CP26,
+ KB_HANYU_PINYIN,
+ KB_THL_PINYIN,
+ KB_MPS2_PINYIN,
+ KB_CARPALX,
+ KB_TYPE_NUM,
+} KB;
+
+typedef enum KeyBehavior {
+ IGNORE = 0,
+ ABSORB,
+ COMMIT,
+ KEY_ERROR,
+ ERROR,
+ NO_WORD,
+ OPEN_SYMBOL_TABLE,
+} KeyBehavior;
+
+typedef enum KeyboardLayoutCompat {
+ DEFAULT = 0,
+ HSU,
+ IBM,
+ GIN_YIEH,
+ ET,
+ ET26,
+ DVORAK,
+ DVORAK_HSU,
+ DACHEN_CP26,
+ HANYU_PINYIN,
+ THL_PINYIN,
+ MPS2_PINYIN,
+ CARPALX,
+} KeyboardLayoutCompat;
+
+enum UserUpdate
+#ifdef __cplusplus
+ : uint8_t
+#endif // __cplusplus
+ {
+ USER_UPDATE_INSERT = 1,
+ USER_UPDATE_MODIFY = 2,
+ USER_UPDATE_FAIL = 4,
+};
+#ifndef __cplusplus
+typedef uint8_t UserUpdate;
+#endif // __cplusplus
+
+typedef struct ChewingConversionEngine ChewingConversionEngine;
+
+/**
+ * A collection of dictionaries that returns the union of the lookup results.
+ * # Examples
+ *
+ * ```
+ * # fn main() -> Result<(), Box<dyn std::error::Error>> {
+ * use std::collections::{HashMap, HashSet};
+ *
+ * use chewing::{dictionary::{LayeredDictionary, Dictionary}, syl, zhuyin::Bopomofo};
+ *
+ * let mut sys_dict = Box::new(HashMap::new());
+ * let mut user_dict = Box::new(HashMap::new());
+ * sys_dict.insert(
+ * vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]],
+ * vec![("測", 1).into(), ("冊", 1).into(), ("側", 1).into()]
+ * );
+ * user_dict.insert(
+ * vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]],
+ * vec![("策", 100).into(), ("冊", 100).into()]
+ * );
+ *
+ * let user_block_list = Box::new(HashSet::from(["側".to_string()]));
+ *
+ * let dict = LayeredDictionary::new(vec![sys_dict, user_dict], vec![user_block_list]);
+ * assert_eq!(
+ * [
+ * ("策", 100).into(),
+ * ("冊", 100).into(),
+ * ("測", 1).into(),
+ * ]
+ * .into_iter()
+ * .collect::<HashSet<_>>(),
+ * dict.lookup_phrase(&[
+ * syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]
+ * ])
+ * .collect::<HashSet<_>>(),
+ * );
+ * # Ok(())
+ * # }
+ * ```
+ */
+typedef struct LayeredDictionary LayeredDictionary;
+
+typedef struct SyllableEditorWithKeymap SyllableEditorWithKeymap;
+
+typedef struct TreeType TreeType;
+
+typedef struct UserphraseDbAndEstimate UserphraseDbAndEstimate;
+
+typedef struct AvailInfoAvail {
+ int len;
+ const void *id;
+} AvailInfoAvail;
+
+/**
+ * Information of available phrase or character choices.
+ */
+typedef struct AvailInfo {
+ /**
+ * All kinds of lengths of available phrases.
+ */
+ struct AvailInfoAvail avail[MAX_PHRASE_LEN];
+ /**
+ * Total number of available lengths.
+ */
+ int nAvail;
+ /**
+ * The current choosing available length.
+ */
+ int currentAvail;
+} AvailInfo;
+
+typedef struct ChoiceInfo {
+ /**
+ * Total page number.
+ */
+ int nPage;
+ /**
+ * Current page number.
+ */
+ int pageNo;
+ /**
+ * Number of choices per page.
+ */
+ int nChoicePerPage;
+ /**
+ * Store possible phrases for being chosen.
+ */
+ char totalChoiceStr[MAX_CHOICE][MAX_PHRASE_UTF8_BUF];
+ /**
+ * Number of phrases to choose.
+ */
+ int nTotalChoice;
+ int oldChiSymbolCursor;
+ int isSymbol;
+} ChoiceInfo;
+
+typedef struct PhrasingOutput {
+ IntervalType dispInterval[MAX_INTERVAL];
+ int nDispInterval;
+ int nNumCut;
+} PhrasingOutput;
+
+typedef struct BopomofoData {
+ struct SyllableEditorWithKeymap *editorWithKeymap;
+} BopomofoData;
+
+typedef struct PreeditBuf {
+ enum Category category;
+ uint8_t char_[MAX_UTF8_BUF];
+} PreeditBuf;
+
+typedef struct UserPhraseData {
+ uint16_t phoneSeq[MAX_PHONE_SEQ_LEN];
+ char wordSeq[MAX_PHRASE_UTF8_BUF];
+ int userfreq;
+ int recentTime;
+ int origfreq;
+ int maxfreq;
+} UserPhraseData;
+
+typedef struct SymbolEntry {
+ /**
+ * Total number of symbols in the category.
+ *
+ * If n_symbols = 0, the category is treat as a symbol,
+ * which is a zero-terminated utf-8 string.
+ *
+ * In that case, `symbols` is unused and isn't allocated at all.
+ */
+ int nSymbols;
+ /**
+ * Category name of the symbols.
+ */
+ char category[MAX_PHRASE_UTF8_BUF];
+ char symbols[0][MAX_UTF8_BUF];
+} SymbolEntry;
+
+typedef struct ChewingStaticData {
+ unsigned int nSymbolEntry;
+ struct SymbolEntry **symbolTable;
+ char *gEasySymbolValue[EASY_SYMBOL_KEY_TAB_LEN];
+ int gEasySymbolNum[EASY_SYMBOL_KEY_TAB_LEN];
+} ChewingStaticData;
+
+typedef struct ChewingData {
+ struct AvailInfo availInfo;
+ struct ChoiceInfo choiceInfo;
+ struct PhrasingOutput phrOut;
+ struct BopomofoData bopomofoData;
+ ChewingConfigData config;
+ /**
+ * Current input buffer, content == 0 means Chinese code
+ */
+ struct PreeditBuf preeditBuf[MAX_PHONE_SEQ_LEN];
+ int chiSymbolCursor;
+ int chiSymbolBufLen;
+ int pointStart;
+ int pointEnd;
+ int bShowMsg;
+ char showMsg[MAX_SHOW_MSG_BUF];
+ int showMsgLen;
+ uint16_t phoneSeq[MAX_PHONE_SEQ_LEN];
+ uint16_t phoneSeqAlt[MAX_PHONE_SEQ_LEN];
+ int nPhoneSeq;
+ char selectStr[MAX_PHONE_SEQ_LEN][MAX_PHRASE_UTF8_BUF];
+ IntervalType selectInterval[MAX_PHONE_SEQ_LEN];
+ int nSelect;
+ IntervalType preferInterval[MAX_INTERVAL];
+ int nPrefer;
+ int bUserArrCnnct[MAX_PHONE_SEQ_BUF];
+ int bUserArrBrkpt[MAX_PHONE_SEQ_BUF];
+ int bArrBrkpt[MAX_PHONE_SEQ_BUF];
+ int bSymbolArrBrkpt[MAX_PHONE_SEQ_BUF];
+ int bChiSym;
+ int bSelect;
+ int bFirstKey;
+ int bFullShape;
+ char symbolKeyBuf[MAX_PHONE_SEQ_LEN];
+ struct UserPhraseData userphraseData;
+ struct ChewingStaticData staticData;
+ void (*logger)(void *data, int level, const char *fmt);
+ void *loggerData;
+ const struct LayeredDictionary *dict;
+ struct ChewingConversionEngine *ce;
+ struct UserphraseDbAndEstimate *ue;
+ void *phraseIter;
+} ChewingData;
+
+typedef struct Phrase {
+ char phrase[MAX_PHONE_SEQ_BUF];
+ int freq;
+} Phrase;
+
+typedef struct ChewingOutput {
+ /**
+ * The content of edit buffer
+ */
+ char preeditBuf[MAX_PHONE_SEQ_UTF8_BUF];
+ /**
+ * The length of edit buffer
+ */
+ int chiSymbolBufLen;
+ /**
+ * The current position of the cursor
+ */
+ long chiSymbolCursor;
+ long pointStart;
+ long pointEnd;
+ char bopomofoBuf[MAX_BOPOMOFO_UTF8_BUF];
+ IntervalType dispInterval[MAX_INTERVAL];
+ int nDispInterval;
+ int dispBrkpt[MAX_PHONE_SEQ_BUF];
+ char commitBuf[MAX_PHONE_SEQ_UTF8_BUF];
+ int commitBufLen;
+ struct ChoiceInfo *pci;
+ int bChiSym;
+ int selKey[MAX_SELKEY];
+ int keystrokeRtn;
+} ChewingOutput;
+
+typedef struct ChewingContext {
+ struct ChewingData *data;
+ struct ChewingOutput *output;
+ int cand_no;
+ int it_no;
+ int kb_no;
+} ChewingContext;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

+extern int toPreeditBufIndex(struct ChewingData *pgdata, int pos);
+
+extern int HaninSymbolInput(struct ChewingData *pgdata);
+
+struct SyllableEditorWithKeymap *NewPhoneticEditor(enum KeyboardLayoutCompat kb_type);
+
+void FreePhoneticEditor(struct SyllableEditorWithKeymap *editor_keymap);
+
+enum KeyBehavior BopomofoPhoInput(struct ChewingData *pgdata, int32_t key);
+
+void BopomofoPhoInx(const struct BopomofoData *bopomofo_data, int32_t *pho_inx);
+
+void BopomofoPhoInxAlt(const struct BopomofoData *bopomofo_data, int32_t *pho_inx);
+
+void BopomofoKeyseq(const struct BopomofoData *bopomofo_data, char *key_seq);
+
+uint16_t BopomofoSyllableIndex(const struct BopomofoData *bopomofo_data);
+
+uint16_t BopomofoSyllableIndexAlt(const struct BopomofoData *bopomofo_data);
+
+uint16_t BopomofoRemoveLast(struct BopomofoData *bopomofo_data);
+
+uint16_t BopomofoRemoveAll(struct BopomofoData *bopomofo_data);
+
+int BopomofoKbType(const struct BopomofoData *bopomofo_data);
+
+uint16_t BopomofoIsEntering(const struct BopomofoData *bopomofo_data);
+
+bool InitPinyin(struct ChewingData *_pgdata, char *_path);
+
+void TerminatePinyin(struct ChewingData *_pgdata);
+
+int InitTree(struct ChewingData *pgdata, const char *_prefix);
+
+void TerminateTree(struct ChewingData *pgdata);
+
+void Phrasing(struct ChewingData *pgdata, bool _all_phrasing);
+
+bool IsIntersect(IntervalType in1, IntervalType in2);
+
+int InitDict(struct ChewingData *pgdata, const char *prefix);
+
+void TerminateDict(struct ChewingData *pgdata);
+
+bool GetCharFirst(struct ChewingData *pgdata, struct Phrase *phrase, uint16_t syllable_u16);
+
+void GetPhraseFirst(struct ChewingData *pgdata,
+ struct Phrase *phrase,
+ const struct TreeType *tree_type);
+
+struct TreeType *TreeFindPhrase(struct ChewingData *pgdata,
+ int begin,
+ int end,
+ uint16_t *syllables_u16);
+
+void FreeTreePhrase(struct TreeType *tree_type);
+
+bool GetVocabNext(struct ChewingData *pgdata, struct Phrase *phrase);
+
uint16_t UintFromPhone(const char *phone);

uint16_t UintFromPhoneInx(const int *ph_inx);
@@ -33,6 +444,67 @@ ptrdiff_t UintArrayFromBopomofo(uint16_t *phone_seq, size_t phone_len, const cha

int GetPhoneLenFromUint(uint16_t phone_num);

+int get_search_path(char *path, size_t path_len);
+
+int find_path_by_files(const char *search_path,
+ const char *const *files,
+ uint8_t *output,
+ size_t output_len);
+
+char *GetDefaultUserPhrasePath(void *_data);
+
+void FreeDefaultUserPhrasePath(char *path);
+
+int InitUserphrase(struct ChewingData *pgdata, char *path);
+
+void TerminateUserphrase(struct ChewingData *pgdata);
+
+const struct UserPhraseData *UserGetPhraseFirst(struct ChewingData *pgdata,
+ uint16_t *syllables_u16_ptr);
+
+const struct UserPhraseData *UserGetPhraseNext(struct ChewingData *pgdata,
+ uint16_t *_syllables_u16_ptr);
+
+void UserGetPhraseEnd(struct ChewingData *pgdata, uint16_t *_syllables_u16_ptr);
+
+uint8_t UserUpdatePhrase(struct ChewingData *pgdata,
+ uint16_t *syllables_u16_ptr,
+ char *phrase_str_ptr);
+
+bool UserRemovePhrase(struct ChewingData *pgdata,
+ uint16_t *syllables_u16_ptr,
+ char *phrase_str_ptr);
+
+void IncreaseLifeTime(struct ChewingData *pgdata);
+
+void UserUpdatePhraseBegin(struct ChewingData *_pgdata);
+
+void UserUpdatePhraseEnd(struct ChewingData *_pgdata);
+
+void *UserEnumeratePhrase(const struct UserphraseDbAndEstimate *ue);
+
+bool UserEnumerateHasNext(void *iter_ptr, unsigned int *phrase_len_ptr, unsigned int *bopomofo_len);
+
+int UserEnumerateGet(void *iter_ptr,
+ char *phrase_buf,
+ const unsigned int *_phrase_len_ptr,
+ char *bopomofo_buf,
+ const unsigned int *_bopomofo_len);
+
+int ueStrLen(const char *str);
+
+int ueBytesFromChar(unsigned char b);
+
+int ueStrNBytes(const char *str, int n);
+
+int ueStrNCpy(char *dest, const char *src, size_t n, int end);
+
+char *ueStrSeek(char *src, size_t n);
+
+const char *ueConstStrSeek(const char *src, size_t n);
+
+const char *ueStrStr(const char *str, size_t _lstr, const char *substr, size_t _lsub);
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
diff --git a/capi/chewing-internal/src/binding.rs b/capi/chewing-internal/src/binding.rs
new file mode 100644
index 0000000..7cc475b
--- /dev/null
+++ b/capi/chewing-internal/src/binding.rs
@@ -0,0 +1,13 @@
+use std::ffi::c_int;
+
+use ffi_opaque::opaque;
+
+opaque! {
+ pub struct ChewingData;
+ pub struct ChewingContext;
+}
+
+extern "C" {
+ pub fn toPreeditBufIndex(pgdata: *mut ChewingData, pos: c_int) -> c_int;
+ pub fn HaninSymbolInput(pgdata: *mut ChewingData) -> c_int;
+}
\ No newline at end of file
diff --git a/capi/chewing-internal/src/bopomofo.rs b/capi/chewing-internal/src/bopomofo.rs
new file mode 100644
index 0000000..70596f6
--- /dev/null
+++ b/capi/chewing-internal/src/bopomofo.rs
@@ -0,0 +1,242 @@
+use std::{
+ ffi::{c_int, CString},
+ os::raw::c_char,
+ slice,
+};
+
+use chewing::editor::{
+ keymap::{
+ IdentityKeymap, KeyCode, KeyCodeFromQwerty, Keymap, RemappingKeymap, CARPALX, DVORAK,
+ QWERTY,
+ },
+ layout::{
+ DaiChien26, Et, Et26, GinYieh, Hsu, Ibm, KeyBehavior, KeyboardLayoutCompat, Pinyin,
+ Standard,
+ },
+ SyllableEditor,
+};
+
+use super::{
+ binding::HaninSymbolInput,
+ types::{BopomofoData, ChewingData},
+};
+
+#[repr(C)]
+pub struct SyllableEditorWithKeymap {
+ kb_type: KeyboardLayoutCompat,
+ keymap: Box<dyn Keymap>,
+ editor: Box<dyn SyllableEditor>,
+}
+
+#[no_mangle]
+pub extern "C" fn NewPhoneticEditor(
+ kb_type: KeyboardLayoutCompat,
+) -> Box<SyllableEditorWithKeymap> {
+ use KeyboardLayoutCompat as KB;
+ match kb_type {
+ KB::Default => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Standard::new()),
+ }),
+ KB::Hsu => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Hsu::new()),
+ }),
+ KB::Ibm => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Ibm::new()),
+ }),
+ KB::GinYieh => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(GinYieh::new()),
+ }),
+ KB::Et => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Et::new()),
+ }),
+ KB::Et26 => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Et26::new()),
+ }),
+ KB::Dvorak => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(RemappingKeymap::new(DVORAK, QWERTY)),
+ editor: Box::new(Standard::new()),
+ }),
+ KB::DvorakHsu => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(RemappingKeymap::new(DVORAK, QWERTY)),
+ editor: Box::new(Hsu::new()),
+ }),
+ KB::DachenCp26 => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(DaiChien26::new()),
+ }),
+ KB::HanyuPinyin => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Pinyin::hanyu()),
+ }),
+ KB::ThlPinyin => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Pinyin::thl()),
+ }),
+ KB::Mps2Pinyin => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(IdentityKeymap::new(QWERTY)),
+ editor: Box::new(Pinyin::mps2()),
+ }),
+ KB::Carpalx => Box::new(SyllableEditorWithKeymap {
+ kb_type,
+ keymap: Box::new(RemappingKeymap::new(CARPALX, QWERTY)),
+ editor: Box::new(Standard::new()),
+ }),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn FreePhoneticEditor(editor_keymap: Option<Box<SyllableEditorWithKeymap>>) {
+ drop(editor_keymap);
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoPhoInput(pgdata: &mut ChewingData, key: i32) -> KeyBehavior {
+ if key == b'`' as i32 {
+ pgdata.b_select = 1;
+ pgdata.choice_info.old_chi_symbol_cursor = pgdata.chi_symbol_cursor;
+ unsafe { HaninSymbolInput((pgdata as *mut ChewingData).cast()) };
+ return KeyBehavior::OpenSymbolTable;
+ }
+
+ let editor_keymap = pgdata.bopomofo_data.editor_with_keymap.as_mut();
+ let key_code = match (key as u8).as_key_code() {
+ Some(key_code) => key_code,
+ None => return KeyBehavior::KeyError,
+ };
+ let key_event = editor_keymap.keymap.map_key(key_code);
+ let result = editor_keymap.editor.key_press(key_event);
+ let key_buf = editor_keymap.editor.read();
+
+ if result == KeyBehavior::Commit {
+ if key_buf.is_empty() {
+ return if key_code == KeyCode::Space {
+ KeyBehavior::KeyError
+ } else {
+ KeyBehavior::NoWord
+ };
+ }
+ // FIXME make sure editors fills the tone
+ // FIXME if dictionary doesn't have a word, return NO_WORD
+ }
+
+ result
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn BopomofoPhoInx(bopomofo_data: &BopomofoData, pho_inx: *mut i32) {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_ref();
+ let pho_inx = unsafe { slice::from_raw_parts_mut(pho_inx, 4) };
+ let syllable = editor_keymap.editor.read();
+
+ pho_inx[0] = match syllable.initial() {
+ Some(b) => b.initial_index() as i32,
+ None => 0,
+ };
+ pho_inx[1] = match syllable.medial() {
+ Some(b) => b.medial_index() as i32,
+ None => 0,
+ };
+ pho_inx[2] = match syllable.rime() {
+ Some(b) => b.rime_index() as i32,
+ None => 0,
+ };
+ pho_inx[3] = match syllable.tone() {
+ Some(b) => b.tone_index() as i32,
+ None => 0,
+ };
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn BopomofoPhoInxAlt(bopomofo_data: &BopomofoData, pho_inx: *mut i32) {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_ref();
+ let pho_inx = unsafe { slice::from_raw_parts_mut(pho_inx, 4) };
+ // FIXME
+ let syllable = editor_keymap.editor.read();
+
+ pho_inx[0] = match syllable.initial() {
+ Some(b) => b.initial_index() as i32,
+ None => 0,
+ };
+ pho_inx[1] = match syllable.medial() {
+ Some(b) => b.medial_index() as i32,
+ None => 0,
+ };
+ pho_inx[2] = match syllable.rime() {
+ Some(b) => b.rime_index() as i32,
+ None => 0,
+ };
+ pho_inx[3] = match syllable.tone() {
+ Some(b) => b.tone_index() as i32,
+ None => 0,
+ };
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoKeyseq(bopomofo_data: &BopomofoData, key_seq: *mut c_char) {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_ref();
+ let key_seq = unsafe { slice::from_raw_parts_mut(key_seq as *mut u8, 10) };
+ if let Some(key_seq_str) = editor_keymap.editor.key_seq() {
+ let key_seq_cstr = CString::new(key_seq_str).unwrap();
+ let key_seq_bytes = key_seq_cstr.as_bytes_with_nul();
+ key_seq[..key_seq_bytes.len()].copy_from_slice(key_seq_bytes);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoSyllableIndex(bopomofo_data: &BopomofoData) -> u16 {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_ref();
+ let syllable = editor_keymap.editor.read();
+ syllable.to_u16()
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoSyllableIndexAlt(bopomofo_data: &BopomofoData) -> u16 {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_ref();
+ // FIXME
+ let syllable = editor_keymap.editor.read();
+ syllable.to_u16()
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoRemoveLast(bopomofo_data: &mut BopomofoData) -> u16 {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_mut();
+ editor_keymap.editor.remove_last();
+ 0
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoRemoveAll(bopomofo_data: &mut BopomofoData) -> u16 {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_mut();
+ editor_keymap.editor.clear();
+ 0
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoKbType(bopomofo_data: &BopomofoData) -> c_int {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_ref();
+ editor_keymap.kb_type as c_int
+}
+
+#[no_mangle]
+pub extern "C" fn BopomofoIsEntering(bopomofo_data: &BopomofoData) -> u16 {
+ let editor_keymap = bopomofo_data.editor_with_keymap.as_ref();
+ u16::from(!editor_keymap.editor.is_empty())
+}
diff --git a/capi/chewing-internal/src/compat.rs b/capi/chewing-internal/src/compat.rs
new file mode 100644
index 0000000..484b9f6
--- /dev/null
+++ b/capi/chewing-internal/src/compat.rs
@@ -0,0 +1,11 @@
+use std::ffi::c_char;
+
+use crate::types::ChewingData;
+
+#[no_mangle]
+pub extern "C" fn InitPinyin(_pgdata: &mut ChewingData, _path: *mut c_char) -> bool {
+ true
+}
+
+#[no_mangle]
+pub extern "C" fn TerminatePinyin(_pgdata: &mut ChewingData) {}
diff --git a/capi/chewing-internal/src/conversion.rs b/capi/chewing-internal/src/conversion.rs
new file mode 100644
index 0000000..2106777
--- /dev/null
+++ b/capi/chewing-internal/src/conversion.rs
@@ -0,0 +1,94 @@
+use std::{
+ ffi::{c_char, c_int, CStr},
+ rc::Rc,
+};
+
+use chewing::conversion::{
+ Break, ChewingConversionEngine, ChineseSequence, ConversionEngine, Interval,
+};
+use chewing_public::types::IntervalType;
+
+use super::{binding::toPreeditBufIndex, types::ChewingData};
+
+#[no_mangle]
+pub extern "C" fn InitTree(pgdata: &mut ChewingData, _prefix: *const c_char) -> c_int {
+ let dict = unsafe {
+ Rc::increment_strong_count(pgdata.dict); <