Skip to content
Commits on Source (3)
DEST/
*.deb
#!/bin/sh
set -x
set -e
print_help_and_exit() {
echo "Usage: ./create-deb.sh
create deb for debian. requires fpm tool."
exit 1
}
# main()
if [ "$1" = "--help" ]; then
print_help_and_exit
fi
DEST=${DEST:-DEST}
VERSION=${VERSION:-`grep -E "^ *version: '.*'" meson.build|cut -d"'" -f2`}
ninja -C release/
mkdir -p \
"$DEST"/usr/share/dbus-1/interfaces/ \
"$DEST"/usr/share/dbus-1/services/ \
"$DEST"/usr/bin/
cp 'com.emacsos.zero.ZeroPinyinService1.ZeroPinyinServiceInterface.xml' "$DEST"/usr/share/dbus-1/interfaces/
cp 'com.emacsos.zero.ZeroPinyinService1.service' "$DEST"/usr/share/dbus-1/services/
sed -i -E 's:/home/sylecn/bin/sbin/zero-pinyin-service:/usr/bin/zero-pinyin-service:' "$DEST"/usr/share/dbus-1/services/com.emacsos.zero.ZeroPinyinService1.service
cp release/zero-pinyin-service "$DEST"/usr/bin/
fpm -f -t deb -s dir -n zero-pinyin-service -v "$VERSION" \
-d libglib2.0-0 \
-d libsqlite3-0 \
-d libstdc++6 \
-d libgcc1 \
-d libc6 \
-d libpcre3 \
-d zlib1g \
-d libselinux1 \
-d libmount1 \
-d libffi6 \
-d libblkid1 \
-d libuuid1 \
-d libpyzy-1.0-0v5 \
--description "provide pinyin input engine for zero-el pinyin" \
--vendor sylecn \
--maintainer "Yuanle Song <sylecn@gmail.com>" \
--deb-priority optional \
--url "https://gitlab.emacsos.com/sylecn/zero-pinyin-service" \
-C "$DEST" .
......@@ -5,12 +5,17 @@
#include "zero-pinyin-service-generated.h"
#include "../sqlite3_util.h"
#include <sqlite3.h>
#include <uuid.h>
static const int MAX_PHRASE_LEN = 16;
static const char *SQLITE3_MEMORY_DB = ":memory:";
typedef struct {
GApplication *app;
guint owner_id;
ZeroPinyinService *interface;
sqlite3 *db;
gchar **env;
} AppData;
static gboolean
......@@ -209,6 +214,176 @@ on_sigterm_received(gpointer user_data)
return G_SOURCE_REMOVE;
}
/**
* return HOME dir, get value from HOME env variable.
*/
static const gchar *
get_home_dir(AppData *appdata)
{
const gchar *result;
result = g_environ_getenv(appdata->env, "HOME");
return result;
}
/**
* return TRUE if file exists
*/
static gboolean
file_exists(const char *filename)
{
return g_file_test(filename, G_FILE_TEST_EXISTS);
}
/**
* Return the file path of the main db file. main db is the main word/phrase
* dababase in libpyzy db format. Without main db, char/phrase query will not
* work at all.
*
* if not main db found, return NULL.
*
* returned gchar* should be freed with g_free().
*/
static gchar *
get_maindb_file(const gchar *home_dir)
{
/* TODO make db path configurable */
const gchar *user_main_db = ".cache/ibus/pinyin/main.db";
const gchar *open_phrase_db = "/usr/share/pyzy/db/open-phrase.db";
const gchar *android_db = "/usr/share/pyzy/db/android.db";
gchar *home_dir_maindb = NULL;
home_dir_maindb = g_strconcat(home_dir, "/", user_main_db, NULL);
if (file_exists(home_dir_maindb)) {
return home_dir_maindb;
}
if (file_exists(open_phrase_db)) {
return g_strdup(open_phrase_db);
}
if (file_exists(android_db)) {
return g_strdup(android_db);
}
return NULL;
}
/**
* return userdb file path.
* "~/.cache/ibus/pinyin/user-1.0.db"
*
* returned gchar* should be freed with g_free().
*/
static gchar *
get_userdb_file(const gchar *home_dir)
{
/* TODO make db path configurable */
return g_strconcat(home_dir, "/.cache/ibus/pinyin/user-1.0.db", NULL);
}
/**
* return a usable userdb file, it's either the path returned by
* `get_userdb_file()' or ':memory:' if that file doesn't exist and can't be
* created.
*
* returned gchar* should be freed with g_free().
*/
static gchar *
get_userdb_file_create(const gchar *home_dir)
{
gchar *userdb_file = NULL;
userdb_file = get_userdb_file(home_dir);
if (file_exists(userdb_file)) {
return userdb_file;
}
/* try create the file */
gchar *parent_dir = NULL;
parent_dir = g_path_get_dirname(userdb_file);
gint r = g_mkdir_with_parents(parent_dir, 0750);
if (r != 0) {
g_warning("create dir %s failed: %d (%s)",
parent_dir, r, g_strerror(r));
return g_strdup(SQLITE3_MEMORY_DB);
}
sqlite3 *userdb = NULL;
g_message("creating user db at %s", userdb_file);
r = sqlite3_open(userdb_file, &userdb);
if (r != SQLITE_OK) {
sqlite3_close(userdb);
return g_strdup(SQLITE3_MEMORY_DB);
}
sqlite3_close(userdb);
return userdb_file;
}
/**
* initialize user db.
* create tables, index and populate data into desc table.
*
* Returns: true on success, false otherwise.
*/
static gboolean
init_userdb(sqlite3 *userdb, const char *schema, AppData *appdata)
{
gboolean rb;
GString *sql;
gchar uuid_str[37];
uuid_t uuid;
gchar *snippet;
/* original libpyzy user db schema */
uuid_generate_random(uuid);
uuid_unparse_lower(uuid, uuid_str);
/* uuid = g_uuid_string_random(); */
sql = g_string_sized_new(200);
snippet = sqlite3_mprintf(
"BEGIN TRANSACTION;\n"
"CREATE TABLE IF NOT EXISTS %s.desc (name PRIMARY KEY, value TEXT);\n"
"INSERT OR IGNORE INTO %s.desc VALUES ('version', '1.2.0');\n"
"INSERT OR IGNORE INTO %s.desc VALUES ('uuid', %Q);\n"
"INSERT OR IGNORE INTO %s.desc VALUES ('hostname', %Q);\n"
"INSERT OR IGNORE INTO %s.desc VALUES ('username', %Q);\n"
"INSERT OR IGNORE INTO %s.desc VALUES ('create-time', datetime());\n"
"INSERT OR IGNORE INTO %s.desc VALUES ('attach-time', datetime());\n", schema, schema, schema, uuid_str, schema, g_environ_getenv(appdata->env, "HOSTNAME"), schema, g_environ_getenv(appdata->env, "USER"), schema, schema);
sql = g_string_append(sql, snippet);
sqlite3_free(snippet);
/* create phrase tables */
for (gint i = 0; i < MAX_PHRASE_LEN; i++) {
g_string_append_printf(sql, "CREATE TABLE IF NOT EXISTS %s.py_phrase_%d (user_freq, phrase TEXT, freq INTEGER", schema, i);
for (gint j = 0; j <= i; j++)
g_string_append_printf(sql, ", s%d INTEGER, y%d INTEGER", j, j);
sql = g_string_append(sql, ");\n");
}
/* create index */
g_string_append_printf(
sql,
"CREATE UNIQUE INDEX IF NOT EXISTS %s.index_0_0 ON py_phrase_0(s0,y0,phrase);\n"
"CREATE UNIQUE INDEX IF NOT EXISTS %s.index_1_0 ON py_phrase_1(s0,y0,s1,y1,phrase);\n"
"CREATE INDEX IF NOT EXISTS %s.index_1_1 ON py_phrase_1(s0,s1,y1);\n", schema, schema, schema);
for (gint i = 2; i < MAX_PHRASE_LEN; i++) {
g_string_append_printf(sql, "CREATE UNIQUE INDEX IF NOT EXISTS %s.index_%d_0 ON py_phrase_%d(s0,y0", schema, i, i);
for (gint j = 1; j <= i; j++)
g_string_append_printf(sql, ",s%d,y%d", j, j);
sql = g_string_append(sql, ",phrase);\n");
g_string_append_printf(sql, "CREATE INDEX IF NOT EXISTS %s.index_%d_1 ON py_phrase_%d(s0,s1,s2,y2);\n", schema, i, i);
}
/* zero-pinyin-service addition */
g_string_append_printf(sql, "CREATE TABLE IF NOT EXISTS %s.not_phrase (phrase TEXT UNIQUE);\n", schema);
sql = g_string_append(sql, "COMMIT;");
rb = sqlite3_exec_simple(userdb, sql->str);
g_string_free(sql, TRUE);
if (! rb) {
g_warning("init userdb failed, query will not work.");
return FALSE;
}
return TRUE;
}
/**
* init appdata->db
*/
......@@ -217,9 +392,14 @@ config_db(AppData *appdata)
{
gint ri = 0;
gboolean rb = FALSE;
static const char *SQLITE3_MEMORY_DB = ":memory:";
sqlite3 *db = NULL;
gchar *sql = NULL;
const gchar *home_dir;
gchar *maindb_file = NULL;
gchar *userdb_file = NULL;
home_dir = get_home_dir(appdata);
ri = sqlite3_open(SQLITE3_MEMORY_DB, &db);
if (ri != SQLITE_OK) {
g_warning("sqlite3_open :memory: db failed, query will not work.");
......@@ -227,9 +407,10 @@ config_db(AppData *appdata)
}
g_assert_nonnull(db);
/* TODO make db path configurable */
/* TODO remove user name in db path */
sql = sqlite3_mprintf("ATTACH %Q AS maindb", "/home/sylecn/.cache/ibus/pinyin/main.db");
maindb_file = get_maindb_file(home_dir);
g_info("using maindb file: %s", maindb_file);
sql = sqlite3_mprintf("ATTACH %Q AS maindb", maindb_file);
g_free(maindb_file);
rb = sqlite3_exec_simple(db, sql);
if (! rb) {
g_warning("attach maindb failed, query will not work.");
......@@ -237,8 +418,9 @@ config_db(AppData *appdata)
}
sqlite3_free(sql);
/* TODO make db path configurable */
sql = sqlite3_mprintf("ATTACH %Q AS userdb", "/home/sylecn/.cache/ibus/pinyin/user-1.0.db");
userdb_file = get_userdb_file_create(home_dir);
sql = sqlite3_mprintf("ATTACH %Q AS userdb", userdb_file);
g_free(userdb_file);
rb = sqlite3_exec_simple(db, sql);
if (! rb) {
g_warning("attach userdb failed, query will not work.");
......@@ -246,13 +428,7 @@ config_db(AppData *appdata)
}
sqlite3_free(sql);
sql = "CREATE TABLE IF NOT EXISTS userdb.not_phrase (phrase TEXT UNIQUE);";
rb = sqlite3_exec_simple(db, sql);
if (! rb) {
g_warning("create userdb.not_phrase table failed, query will not work.");
sql = NULL;
goto attach_fail;
}
init_userdb(db, "userdb", appdata);
appdata->db = db;
return;
......@@ -287,6 +463,7 @@ on_startup(GApplication *app,
AppData *appdata)
{
g_message("zero-pinyin-service startup()");
appdata->env = g_get_environ();
config_db(appdata);
config_dbus_service(appdata);
setup_sigint_sigterm_handler(appdata);
......@@ -313,6 +490,9 @@ on_shutdown(GApplication *app,
sqlite3_close(appdata->db);
appdata->db = NULL;
}
if (appdata->env != NULL) {
g_strfreev(appdata->env);
}
}
/**
......
# -*- mode: conf -*-
project('zero-pinyin-service', ['c', 'cpp'],
version: '0.6.1',
version: '0.7.1',
license: 'GPL',
default_options: [
'warning_level=2',
......
* COMMENT -*- mode: org -*-
#+Date: 2019-04-05
Time-stamp: <2019-04-17>
Time-stamp: <2019-08-31>
#+STARTUP: content
* notes :entry:
** 2019-08-31 ibus-pinyin userdb inference notice.
zero-pinyin-service reuse ibus-pinyin's userdb at
~/.cache/ibus/pinyin/user-1.0.db
This is generally not a problem. But if ibus-pinyin changed their table schema
in the future, zero-pinyin-service may require update.
zero-pinyin-service also store user phrase in this db. So user phrases are
shared between zero-pinyin and ibus-pinyin.
** 2019-04-05 zero-pinyin-service file structure :doc:
- zero-pinyin-service
- main.c
......@@ -42,9 +52,50 @@ Time-stamp: <2019-04-17>
* later :entry:
* current :entry:
**
** 2019-08-31 how to format C code? do it before git commit.
see ~/c/gtk-im-module/, it uses myastyle-pre-commit-check in git pre-commit
~/bin/myastyle-pre-commit-check
** 2019-08-31 honor XDG cache dir.
~/.cache/ibus
** 2019-04-17 make flags configurable at runtime.
- add dbus method to set flags.
- make the method work. use gobject property maybe.
- set default flags to my flags. reflect this in UI/config file.
* done :entry:
** 2019-08-31 choose maindb like my patched libpyzy.
- here is patched libpyzy maindb logic:
files.push_back (m_user_data_dir + "/main.db");
files.push_back (PKGDATADIR"/db/local.db");
files.push_back (PKGDATADIR"/db/open-phrase.db");
files.push_back (PKGDATADIR"/db/android.db");
return first_existing_file (files);
m_user_data_dir default is ~/.cache/ibus/pinyin/
PKGDATADIR default is /usr/share/pyzy/
in zero-pinyin-service, just use the first existing file:
~/.cache/ibus/pinyin/main.db
/usr/share/pyzy/db/open-phrase.db
/usr/share/pyzy/db/android.db
- should I reuse the ibus-pinyin userdb file?
~/.cache/ibus/pinyin/user-1.0.db
yes. ibus-pinyin is not going away.
DONE document this behavior in zero-el and zero-pinyin-service.
- init_userdb()
sqlite3_mprintf()
https://www.sqlite.org/c3ref/mprintf.html
additional non-standard formats (%q, %Q, %w, and %z).
| | in | out | used for |
|----+------+---------+----------------------------------------------------------|
| %q | ab'c | ab''c | SQL string literal |
| %Q | ab'c | 'ab''c' | SQL string literal |
| %w | ab"c | ab""c | SQL identifier name |
| %z | abc | abc | like %s, but sqlite3_free() is called on param after use |
* wontfix :entry: