summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am11
-rwxr-xr-xandroid/build.py1
-rw-r--r--configure.ac13
-rw-r--r--src/Main.cxx8
-rw-r--r--src/SongFilter.cxx35
-rw-r--r--src/db/Directory.cxx6
-rw-r--r--src/db/SongSort.cxx7
-rw-r--r--src/lib/icu/Collate.cxx170
-rw-r--r--src/lib/icu/Collate.hxx44
-rw-r--r--src/lib/icu/Error.cxx24
-rw-r--r--src/lib/icu/Error.hxx29
11 files changed, 310 insertions, 38 deletions
diff --git a/Makefile.am b/Makefile.am
index c02ca7ce7..aa52c68ef 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,6 +16,7 @@ noinst_LIBRARIES = \
libthread.a \
libsystem.a \
libevent.a \
+ libicu.a \
libpcm.a \
libconf.a \
libtag.a \
@@ -56,6 +57,7 @@ src_mpd_LDADD = \
libevent.a \
libthread.a \
libsystem.a \
+ $(ICU_LDADD) \
libutil.a \
libfs.a \
$(SYSTEMD_DAEMON_LIBS) \
@@ -410,6 +412,14 @@ libevent_a_SOURCES = \
src/event/Call.hxx src/event/Call.cxx \
src/event/Loop.cxx src/event/Loop.hxx
+# UTF-8 library
+
+libicu_a_SOURCES = \
+ src/lib/icu/Collate.cxx src/lib/icu/Collate.hxx \
+ src/lib/icu/Error.cxx src/lib/icu/Error.hxx
+
+ICU_LDADD = libicu.a $(ICU_LIBS)
+
# PCM library
libpcm_a_SOURCES = \
@@ -1426,6 +1436,7 @@ test_DumpDatabase_LDADD = \
libevent.a \
libsystem.a \
libfs.a \
+ $(ICU_LDADD) \
$(GLIB_LIBS)
test_DumpDatabase_SOURCES = test/DumpDatabase.cxx \
src/protocol/Ack.cxx \
diff --git a/android/build.py b/android/build.py
index 090d47769..5898d0f91 100755
--- a/android/build.py
+++ b/android/build.py
@@ -319,6 +319,7 @@ configure = [
'--enable-silent-rules',
'--disable-glib',
+ '--disable-icu',
# disabled for now because these features require GLib:
'--disable-database',
diff --git a/configure.ac b/configure.ac
index f05c17b2a..f449c2d2e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -627,6 +627,19 @@ dnl ---------------------------------------------------------------------------
dnl Mandatory Libraries
dnl ---------------------------------------------------------------------------
+AC_ARG_ENABLE(icu,
+ AS_HELP_STRING([--enable-icu],
+ [enable libicu for Unicode (default: enabled)]),,
+ enable_icu=yes)
+
+if test x$enable_icu = xyes; then
+ PKG_CHECK_MODULES([ICU], [icu-i18n],,
+ [AC_MSG_ERROR([libicu not found])])
+
+ AC_DEFINE(HAVE_ICU, 1, [Define if libicu is used])
+fi
+AM_CONDITIONAL(HAVE_ICU, test x$enable_icu = xyes)
+
AC_ARG_ENABLE(glib,
AS_HELP_STRING([--enable-glib],
[enable GLib usage (default: enabled)]),,
diff --git a/src/Main.cxx b/src/Main.cxx
index fe91ed3a2..4c7c6d97c 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -57,6 +57,7 @@
#include "util/Domain.hxx"
#include "thread/Id.hxx"
#include "thread/Slack.hxx"
+#include "lib/icu/Collate.hxx"
#include "config/ConfigGlobal.hxx"
#include "config/ConfigData.hxx"
#include "config/ConfigDefaults.hxx"
@@ -405,6 +406,11 @@ int mpd_main(int argc, char *argv[])
#endif
#endif
+ if (!IcuCollateInit(error)) {
+ LogError(error);
+ return EXIT_FAILURE;
+ }
+
winsock_init();
io_thread_init();
config_global_init();
@@ -651,6 +657,8 @@ int mpd_main(int argc, char *argv[])
WSACleanup();
#endif
+ IcuCollateFinish();
+
log_deinit();
return EXIT_SUCCESS;
}
diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx
index 77fea606e..03ff3991e 100644
--- a/src/SongFilter.cxx
+++ b/src/SongFilter.cxx
@@ -25,10 +25,7 @@
#include "tag/Tag.hxx"
#include "util/ASCII.hxx"
#include "util/UriUtil.hxx"
-
-#ifdef HAVE_GLIB
-#include <glib.h>
-#endif
+#include "lib/icu/Collate.hxx"
#include <assert.h>
#include <string.h>
@@ -56,25 +53,10 @@ locate_parse_type(const char *str)
gcc_pure
static std::string
-CaseFold(const char *p)
-{
-#ifdef HAVE_GLIB
- char *q = g_utf8_casefold(p, -1);
- std::string result(q);
- g_free(q);
- return result;
-#else
- // TODO: implement without GLib
- return p;
-#endif
-}
-
-gcc_pure
-static std::string
ImportString(const char *p, bool fold_case)
{
return fold_case
- ? CaseFold(p)
+ ? IcuCaseFold(p)
: std::string(p);
}
@@ -90,17 +72,8 @@ SongFilter::Item::StringMatch(const char *s) const
assert(s != nullptr);
if (fold_case) {
-#ifdef HAVE_GLIB
- char *p = g_utf8_casefold(s, -1);
-#else
- // TODO: implement without GLib
- const char *p = s;
-#endif
- const bool result = strstr(p, value.c_str()) != NULL;
-#ifdef HAVE_GLIB
- g_free(p);
-#endif
- return result;
+ const std::string folded = IcuCaseFold(s);
+ return folded.find(value) != folded.npos;
} else {
return s == value;
}
diff --git a/src/db/Directory.cxx b/src/db/Directory.cxx
index 01e147eff..1da19be98 100644
--- a/src/db/Directory.cxx
+++ b/src/db/Directory.cxx
@@ -27,6 +27,7 @@
#include "SongSort.hxx"
#include "Song.hxx"
#include "LightSong.hxx"
+#include "lib/icu/Collate.hxx"
#include "fs/Traits.hxx"
#include "util/Alloc.hxx"
#include "util/Error.hxx"
@@ -35,8 +36,6 @@ extern "C" {
#include "util/list_sort.h"
}
-#include <glib.h>
-
#include <assert.h>
#include <string.h>
#include <stdlib.h>
@@ -229,7 +228,8 @@ directory_cmp(gcc_unused void *priv,
{
const Directory *a = (const Directory *)_a;
const Directory *b = (const Directory *)_b;
- return g_utf8_collate(a->path.c_str(), b->path.c_str());
+
+ return IcuCollate(a->path.c_str(), b->path.c_str());
}
void
diff --git a/src/db/SongSort.cxx b/src/db/SongSort.cxx
index dcea033b6..c5752f568 100644
--- a/src/db/SongSort.cxx
+++ b/src/db/SongSort.cxx
@@ -21,13 +21,12 @@
#include "SongSort.hxx"
#include "Song.hxx"
#include "tag/Tag.hxx"
+#include "lib/icu/Collate.hxx"
extern "C" {
#include "util/list_sort.h"
}
-#include <glib.h>
-
#include <stdlib.h>
static int
@@ -39,7 +38,7 @@ compare_utf8_string(const char *a, const char *b)
if (b == nullptr)
return 1;
- return g_utf8_collate(a, b);
+ return IcuCollate(a, b);
}
/**
@@ -104,7 +103,7 @@ song_cmp(gcc_unused void *priv, struct list_head *_a, struct list_head *_b)
return ret;
/* still no difference? compare file name */
- return g_utf8_collate(a->uri, b->uri);
+ return IcuCollate(a->uri, b->uri);
}
void
diff --git a/src/lib/icu/Collate.cxx b/src/lib/icu/Collate.cxx
new file mode 100644
index 000000000..8dd757fb0
--- /dev/null
+++ b/src/lib/icu/Collate.cxx
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "Collate.hxx"
+
+#ifdef HAVE_ICU
+#include "Error.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+#elif defined(HAVE_GLIB)
+#include <glib.h>
+#else
+#include <algorithm>
+#include <ctype.h>
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#ifdef HAVE_ICU
+static UCollator *collator;
+#endif
+
+bool
+IcuCollateInit(Error &error)
+{
+#ifdef HAVE_ICU
+ assert(collator == nullptr);
+ assert(!error.IsDefined());
+
+ UErrorCode code;
+ collator = ucol_open("", &code);
+ if (collator == nullptr) {
+ error.Format(icu_domain, int(code),
+ "ucol_open() failed: %s", u_errorName(code));
+ return false;
+ }
+#else
+ (void)error;
+#endif
+
+ return true;
+}
+
+void
+IcuCollateFinish()
+{
+#ifdef HAVE_ICU
+ assert(collator != nullptr);
+
+ ucol_close(collator);
+#endif
+}
+
+#ifdef HAVE_ICU
+
+static UChar *
+UCharFromUTF8(const char *src, int32_t *dest_length)
+{
+ assert(src != nullptr);
+
+ const size_t src_length = strlen(src);
+ size_t dest_capacity = src_length + 1;
+ UChar *dest = new UChar[dest_capacity];
+
+ UErrorCode error_code;
+ u_strFromUTF8(dest, dest_capacity,
+ dest_length,
+ src, src_length,
+ &error_code);
+ if (U_FAILURE(error_code)) {
+ delete[] dest;
+ return nullptr;
+ }
+
+ return dest;
+}
+
+#endif
+
+gcc_pure
+int
+IcuCollate(const char *a, const char *b)
+{
+ assert(a != nullptr);
+ assert(b != nullptr);
+
+#ifdef HAVE_ICU
+ assert(collator != nullptr);
+
+#if U_ICU_VERSION_MAJOR_NUM >= 50
+ return (int)ucol_strcollUTF8(collator, a, -1, b, -1, nullptr);
+#else
+ /* fall back to ucol_strcoll() */
+
+ UChar *au = UCharFromUTF8(a, nullptr);
+ UChar *bu = UCharFromUTF8(b, nullptr);
+
+ int result = au != nullptr && bu != nullptr
+ ? (int)ucol_strcoll(collator, au, -1, bu, -1)
+ : strcasecmp(a, b);
+
+ delete[] au;
+ delete[] bu;
+
+ return result;
+#endif
+
+#elif defined(HAVE_GLIB)
+ return g_utf8_collate(a, b);
+#else
+ return strcasecmp(a, b);
+#endif
+}
+
+std::string
+IcuCaseFold(const char *src)
+{
+#ifdef HAVE_ICU
+ assert(collator != nullptr);
+ assert(src != nullptr);
+
+ int32_t u_length;
+ UChar *u = UCharFromUTF8(src, &u_length);
+ if (u == nullptr)
+ return std::string(src);
+
+ size_t dest_length = ucol_getSortKey(collator, u, u_length,
+ nullptr, 0);
+ if (dest_length == 0) {
+ delete[] u;
+ return std::string(src);
+ }
+
+ uint8_t *dest = new uint8_t[dest_length];
+ ucol_getSortKey(collator, u, u_length,
+ dest, dest_length);
+ std::string result((const char *)dest);
+ delete[] dest;
+#elif defined(HAVE_GLIB)
+ char *tmp = g_utf8_casefold(src, -1);
+ std::string result(tmp);
+ g_free(tmp);
+#else
+ std::string result(src);
+ std::transform(result.begin(), result.end(), result.begin(), tolower);
+#endif
+ return result;
+}
+
diff --git a/src/lib/icu/Collate.hxx b/src/lib/icu/Collate.hxx
new file mode 100644
index 000000000..8ae8de46a
--- /dev/null
+++ b/src/lib/icu/Collate.hxx
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ICU_COLLATE_HXX
+#define MPD_ICU_COLLATE_HXX
+
+#include "check.h"
+#include "Compiler.h"
+
+#include <string>
+
+class Error;
+
+bool
+IcuCollateInit(Error &error);
+
+void
+IcuCollateFinish();
+
+gcc_pure gcc_nonnull_all
+int
+IcuCollate(const char *a, const char *b);
+
+gcc_pure gcc_nonnull_all
+std::string
+IcuCaseFold(const char *src);
+
+#endif
diff --git a/src/lib/icu/Error.cxx b/src/lib/icu/Error.cxx
new file mode 100644
index 000000000..1fef078ac
--- /dev/null
+++ b/src/lib/icu/Error.cxx
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "Error.hxx"
+#include "util/Domain.hxx"
+
+const Domain icu_domain("icu");
diff --git a/src/lib/icu/Error.hxx b/src/lib/icu/Error.hxx
new file mode 100644
index 000000000..e96667f57
--- /dev/null
+++ b/src/lib/icu/Error.hxx
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ICU_ERROR_HXX
+#define MPD_ICU_ERROR_HXX
+
+#include "check.h"
+
+class Domain;
+
+extern const Domain icu_domain;
+
+#endif