mediascanner2-0.100+14.04.20140403/0000755000015301777760000000000012317231021016610 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/cmake/0000755000015301777760000000000012317231021017670 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/cmake/coverage.cmake0000644000015301777760000000323012317230414022470 0ustar pbusernogroup00000000000000if (CMAKE_BUILD_TYPE MATCHES coverage) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} --coverage") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") find_program(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin") if (NOT GCOVR_EXECUTABLE) message(STATUS "Gcovr binary was not found, can not generate XML coverage info.") else () message(STATUS "Gcovr found, can generate XML coverage info.") add_custom_target (coverage-xml WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND "${GCOVR_EXECUTABLE}" --exclude="test.*" -x -r "${CMAKE_SOURCE_DIR}" --object-directory=${CMAKE_BINARY_DIR} -o coverage.xml) endif() find_program(LCOV_EXECUTABLE lcov HINTS ${LCOV_ROOT} "${GCOVR_ROOT}/bin") find_program(GENHTML_EXECUTABLE genhtml HINTS ${GENHTML_ROOT}) if (NOT LCOV_EXECUTABLE) message(STATUS "Lcov binary was not found, can not generate HTML coverage info.") else () if(NOT GENHTML_EXECUTABLE) message(STATUS "Genthml binary not found, can not generate HTML coverage info.") else() message(STATUS "Lcov and genhtml found, can generate HTML coverage info.") add_custom_target (coverage-html WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture --output-file coverage.info --no-checksum COMMAND "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info ) endif() endif() endif() mediascanner2-0.100+14.04.20140403/test/0000755000015301777760000000000012317231021017567 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/test/test_mediastore.cc0000644000015301777760000004274412317230414023311 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include #include #include #include #include #include #include #include using namespace std; using namespace mediascanner; class MediaStoreTest : public ::testing::Test { protected: MediaStoreTest() { } virtual ~MediaStoreTest() { } virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(MediaStoreTest, init) { MediaStore store(":memory:", MS_READ_WRITE); } TEST_F(MediaStoreTest, mediafile_uri) { MediaFile media("/path/to/file.ogg"); EXPECT_EQ(media.getUri(), "file:///path/to/file.ogg"); } TEST_F(MediaStoreTest, equality) { MediaFile audio1("a", "type", "etag", "1900", "b", "c", "d", "e", 1, 5, AudioMedia); MediaFile audio2("aa", "type", "etag", "1900", "b", "c", "d", "e", 1, 5, AudioMedia); MediaFile video1("a", "type", "etag", "b", "1900", "c", "d", "e", 0, 5, VideoMedia); MediaFile video2("aa", "type", "etag", "b", "1900", "c", "d", "e", 0, 5, VideoMedia); EXPECT_EQ(audio1, audio1); EXPECT_EQ(video1, video1); EXPECT_NE(audio1, audio2); EXPECT_NE(audio1, video1); EXPECT_NE(audio2, video1); EXPECT_NE(audio2, video2); } TEST_F(MediaStoreTest, lookup) { MediaFile audio("aaa", "type", "etag", "bbb bbb", "1900-01-01", "ccc", "ddd", "eee", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio); EXPECT_EQ(store.lookup("aaa"), audio); EXPECT_THROW(store.lookup("not found"), std::runtime_error); } TEST_F(MediaStoreTest, roundtrip) { MediaFile audio("aaa", "type", "etag", "bbb bbb", "1900-01-01", "ccc", "ddd", "eee", 3, 5, AudioMedia); MediaFile video("aaa2", "type", "etag", "bbb bbb", "2012-01-01", "ccc", "ddd", "eee", 0, 5, VideoMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio); store.insert(video); vector result = store.query("bbb", AudioMedia); ASSERT_EQ(result.size(), 1); EXPECT_EQ(result[0], audio); result = store.query("bbb", VideoMedia); ASSERT_EQ(result.size(), 1); EXPECT_EQ(result[0], video); } TEST_F(MediaStoreTest, query_by_album) { MediaFile audio("/path/foo.ogg", "", "", "title", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio); vector result = store.query("album", AudioMedia); ASSERT_EQ(result.size(), 1); EXPECT_EQ(result[0], audio); } TEST_F(MediaStoreTest, query_by_artist) { MediaFile audio("/path/foo.ogg", "", "", "title", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio); vector result = store.query("artist", AudioMedia); ASSERT_EQ(result.size(), 1); EXPECT_EQ(result[0], audio); } TEST_F(MediaStoreTest, query_ranking) { MediaFile audio1("/path/foo1.ogg", "", "", "title", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaFile audio2("/path/foo2.ogg", "", "", "title aaa", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaFile audio3("/path/foo3.ogg", "", "", "title", "1900-01-01", "artist aaa", "album", "albumartist", 3, 5, AudioMedia); MediaFile audio4("/path/foo4.ogg", "", "", "title", "1900-01-01", "artist", "album aaa", "albumartist", 3, 5, AudioMedia); MediaFile audio5("/path/foo5.ogg", "", "", "title aaa", "1900-01-01", "artist aaa", "album aaa", "albumartist", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); store.insert(audio4); store.insert(audio5); vector result = store.query("aaa", AudioMedia); ASSERT_EQ(result.size(), 4); EXPECT_EQ(result[0], audio5); // Term appears in title, artist and album EXPECT_EQ(result[1], audio2); // title has highest weighting EXPECT_EQ(result[2], audio4); // then album EXPECT_EQ(result[3], audio3); // then artist } TEST_F(MediaStoreTest, query_limit) { MediaFile audio1("/path/foo5.ogg", "", "", "title aaa", "1900-01-01", "artist aaa", "album aaa", "albumartist", 3, 5, AudioMedia); MediaFile audio2("/path/foo2.ogg", "", "", "title aaa", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaFile audio3("/path/foo4.ogg", "", "", "title", "1900-01-01", "artist", "album aaa", "albumartist", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); vector result = store.query("aaa", AudioMedia, 2); ASSERT_EQ(result.size(), 2); EXPECT_EQ(result[0], audio1); // Term appears in title, artist and album EXPECT_EQ(result[1], audio2); // title has highest weighting } TEST_F(MediaStoreTest, query_short) { MediaFile audio1("/path/foo5.ogg", "", "", "title xyz", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaFile audio2("/path/foo2.ogg", "", "", "title xzy", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); vector result = store.query("x", AudioMedia); EXPECT_EQ(result.size(), 2); result = store.query("xy", AudioMedia); EXPECT_EQ(result.size(), 1); } TEST_F(MediaStoreTest, query_empty) { MediaFile audio1("/path/foo5.ogg", "", "", "title aaa", "1900-01-01", "artist aaa", "album aaa", "albumartist", 3, 5, AudioMedia); MediaFile audio2("/path/foo2.ogg", "", "", "title aaa", "1900-01-01", "artist", "album", "albumartist", 3, 5, AudioMedia); MediaFile audio3("/path/foo4.ogg", "", "", "title", "1900-01-01", "artist", "album aaa", "albumartist", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); // An empty query should return some results vector result = store.query("", AudioMedia, 2); ASSERT_EQ(result.size(), 2); } TEST_F(MediaStoreTest, unmount) { MediaFile audio1("/media/username/dir/fname.ogg", "", "", "bbb bbb", "2000-01-01", "ccc", "ddd", "eee", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/fname.ogg", "", "", "bbb bbb", "1900-01-01", "ccc", "ddd", "eee", 42, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); vector result = store.query("bbb", AudioMedia); ASSERT_EQ(result.size(), 2); store.archiveItems("/media/username"); result = store.query("bbb", AudioMedia); ASSERT_EQ(result.size(), 1); EXPECT_EQ(result[0], audio2); store.restoreItems("/media/username"); result = store.query("bbb", AudioMedia); ASSERT_EQ(result.size(), 2); } TEST_F(MediaStoreTest, utils) { string source("_a.b(c)[d]{e}f.mp3"); string correct = {" a b c d e f"}; string result = filenameToTitle(source); EXPECT_EQ(correct, result); string unquoted(R"(It's a living.)"); string quoted(R"('It''s a living.')"); EXPECT_EQ(sqlQuote(unquoted), quoted); } TEST_F(MediaStoreTest, queryAlbums) { MediaFile audio1("/home/username/Music/track1.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumOne", "Various Artists", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/track2.ogg", "", "", "TitleTwo", "1900-01-01", "ArtistTwo", "AlbumOne", "Various Artists", 2, 5, AudioMedia); MediaFile audio3("/home/username/Music/track3.ogg", "", "", "TitleThree", "1900-01-01", "ArtistThree", "AlbumOne", "Various Artists", 3, 5, AudioMedia); MediaFile audio4("/home/username/Music/fname.ogg", "", "", "TitleFour", "1900-01-01", "ArtistFour", "AlbumTwo", "ArtistFour", 1, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); store.insert(audio4); // Query a track title vector albums = store.queryAlbums("TitleOne"); ASSERT_EQ(albums.size(), 1); EXPECT_EQ(albums[0].getTitle(), "AlbumOne"); EXPECT_EQ(albums[0].getArtist(), "Various Artists"); // Query an album name albums = store.queryAlbums("AlbumTwo"); ASSERT_EQ(albums.size(), 1); EXPECT_EQ(albums[0].getTitle(), "AlbumTwo"); EXPECT_EQ(albums[0].getArtist(), "ArtistFour"); // Query an artist name albums = store.queryAlbums("ArtistTwo"); ASSERT_EQ(albums.size(), 1); EXPECT_EQ(albums[0].getTitle(), "AlbumOne"); EXPECT_EQ(albums[0].getArtist(), "Various Artists"); } TEST_F(MediaStoreTest, queryAlbums_limit) { MediaFile audio1("/home/username/Music/track1.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumOne", "Various Artists", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/track2.ogg", "", "", "TitleTwo", "1900-01-01", "ArtistTwo", "AlbumOne", "Various Artists", 2, 5, AudioMedia); MediaFile audio3("/home/username/Music/track3.ogg", "", "", "TitleThree", "1900-01-01", "ArtistThree", "AlbumOne", "Various Artists", 3, 5, AudioMedia); MediaFile audio4("/home/username/Music/fname.ogg", "", "", "TitleFour", "1900-01-01", "ArtistFour", "AlbumTwo", "ArtistFour", 1, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); store.insert(audio4); vector albums = store.queryAlbums("Artist"); EXPECT_EQ(2, albums.size()); albums = store.queryAlbums("Artist", 1); EXPECT_EQ(1, albums.size()); } TEST_F(MediaStoreTest, queryAlbums_empty) { MediaFile audio1("/home/username/Music/track1.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumOne", "Various Artists", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/track2.ogg", "", "", "TitleTwo", "1900-01-01", "ArtistTwo", "AlbumOne", "Various Artists", 2, 5, AudioMedia); MediaFile audio3("/home/username/Music/track3.ogg", "", "", "TitleThree", "1900-01-01", "ArtistThree", "AlbumOne", "Various Artists", 3, 5, AudioMedia); MediaFile audio4("/home/username/Music/fname.ogg", "", "", "TitleFour", "1900-01-01", "ArtistFour", "AlbumTwo", "ArtistFour", 1, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); store.insert(audio4); vector albums = store.queryAlbums(""); EXPECT_EQ(2, albums.size()); albums = store.queryAlbums("", 1); EXPECT_EQ(1, albums.size()); } TEST_F(MediaStoreTest, getAlbumSongs) { MediaFile audio1("/home/username/Music/track1.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumOne", "Various Artists", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/track2.ogg", "", "", "TitleTwo", "1900-01-01", "ArtistTwo", "AlbumOne", "Various Artists", 2, 5, AudioMedia); MediaFile audio3("/home/username/Music/track3.ogg", "", "", "TitleThree", "1900-01-01", "ArtistThree", "AlbumOne", "Various Artists", 3, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); vector tracks = store.getAlbumSongs( Album("AlbumOne", "Various Artists")); ASSERT_EQ(tracks.size(), 3); EXPECT_EQ(tracks[0].getTitle(), "TitleOne"); EXPECT_EQ(tracks[1].getTitle(), "TitleTwo"); EXPECT_EQ(tracks[2].getTitle(), "TitleThree"); } TEST_F(MediaStoreTest, getETag) { MediaFile file("/path/file.ogg", "audio/ogg", "etag", "title", "2013", "artist", "album", "artist", 1, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(file); EXPECT_EQ(store.getETag("/path/file.ogg"), "etag"); EXPECT_EQ(store.getETag("/something-else.mp3"), ""); } TEST_F(MediaStoreTest, listSongs) { MediaFile audio1("/home/username/Music/track1.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumOne", "ArtistOne", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/track2.ogg", "", "", "TitleTwo", "1900-01-01", "ArtistOne", "AlbumOne", "ArtistOne", 2, 5, AudioMedia); MediaFile audio3("/home/username/Music/track3.ogg", "", "", "TitleThree", "1900-01-01", "ArtistOne", "AlbumTwo", "ArtistOne", 3, 5, AudioMedia); MediaFile audio4("/home/username/Music/track4.ogg", "", "", "TitleFour", "1900-01-01", "ArtistTwo", "AlbumThree", "ArtistTwo", 1, 5, AudioMedia); MediaFile audio5("/home/username/Music/track5.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumFour", "Various Artists", 1, 5, AudioMedia); MediaFile audio6("/home/username/Music/track6.ogg", "", "", "TitleFour", "1900-01-01", "ArtistTwo", "AlbumFour", "Various Artists", 2, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); store.insert(audio4); store.insert(audio5); store.insert(audio6); vector tracks = store.listSongs(); ASSERT_EQ(6, tracks.size()); EXPECT_EQ("TitleOne", tracks[0].getTitle()); // Apply a limit tracks = store.listSongs("", "", "", 4); EXPECT_EQ(4, tracks.size()); // List songs by artist tracks = store.listSongs("ArtistOne"); EXPECT_EQ(4, tracks.size()); // List songs by album tracks = store.listSongs("", "AlbumOne"); EXPECT_EQ(2, tracks.size()); // List songs by album artist tracks = store.listSongs("", "", "Various Artists"); EXPECT_EQ(2, tracks.size()); // Combinations tracks = store.listSongs("ArtistOne", "AlbumOne", ""); EXPECT_EQ(2, tracks.size()); tracks = store.listSongs("", "AlbumOne", "ArtistOne"); EXPECT_EQ(2, tracks.size()); tracks = store.listSongs("ArtistOne", "AlbumOne", "ArtistOne"); EXPECT_EQ(2, tracks.size()); tracks = store.listSongs("ArtistOne", "", "ArtistOne"); EXPECT_EQ(3, tracks.size()); } TEST_F(MediaStoreTest, listAlbums) { MediaFile audio1("/home/username/Music/track1.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumOne", "ArtistOne", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/track3.ogg", "", "", "TitleThree", "1900-01-01", "ArtistOne", "AlbumTwo", "ArtistOne", 3, 5, AudioMedia); MediaFile audio3("/home/username/Music/track4.ogg", "", "", "TitleFour", "1900-01-01", "ArtistTwo", "AlbumThree", "ArtistTwo", 1, 5, AudioMedia); MediaFile audio4("/home/username/Music/track5.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumFour", "Various Artists", 1, 5, AudioMedia); MediaFile audio5("/home/username/Music/track6.ogg", "", "", "TitleFour", "1900-01-01", "ArtistTwo", "AlbumFour", "Various Artists", 2, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); store.insert(audio4); store.insert(audio5); vector albums = store.listAlbums(); ASSERT_EQ(4, albums.size()); EXPECT_EQ("AlbumOne", albums[0].getTitle()); // test limit albums = store.listAlbums("", "", 2); EXPECT_EQ(2, albums.size()); // Songs by artist albums = store.listAlbums("ArtistOne"); EXPECT_EQ(3, albums.size()); // Songs by album artist albums = store.listAlbums("", "ArtistOne"); EXPECT_EQ(2, albums.size()); // Combination albums = store.listAlbums("ArtistOne", "Various Artists"); EXPECT_EQ(1, albums.size()); } TEST_F(MediaStoreTest, listArtists) { MediaFile audio1("/home/username/Music/track1.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumOne", "ArtistOne", 1, 5, AudioMedia); MediaFile audio2("/home/username/Music/track3.ogg", "", "", "TitleThree", "1900-01-01", "ArtistOne", "AlbumTwo", "ArtistOne", 3, 5, AudioMedia); MediaFile audio3("/home/username/Music/track4.ogg", "", "", "TitleFour", "1900-01-01", "ArtistTwo", "AlbumThree", "ArtistTwo", 1, 5, AudioMedia); MediaFile audio4("/home/username/Music/track5.ogg", "", "", "TitleOne", "1900-01-01", "ArtistOne", "AlbumFour", "Various Artists", 1, 5, AudioMedia); MediaFile audio5("/home/username/Music/track6.ogg", "", "", "TitleFour", "1900-01-01", "ArtistTwo", "AlbumFour", "Various Artists", 2, 5, AudioMedia); MediaStore store(":memory:", MS_READ_WRITE); store.insert(audio1); store.insert(audio2); store.insert(audio3); store.insert(audio4); store.insert(audio5); vector artists = store.listArtists(false); ASSERT_EQ(2, artists.size()); EXPECT_EQ("ArtistOne", artists[0]); EXPECT_EQ("ArtistTwo", artists[1]); // Test limit clause artists = store.listArtists(false, 1); EXPECT_EQ(1, artists.size()); // List "album artists" artists = store.listArtists(true); ASSERT_EQ(3, artists.size()); EXPECT_EQ("ArtistOne", artists[0]); EXPECT_EQ("ArtistTwo", artists[1]); EXPECT_EQ("Various Artists", artists[2]); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } mediascanner2-0.100+14.04.20140403/test/test_metadataextractor.cc0000644000015301777760000000607212317230414024663 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include #include #include "test_config.h" #include #include #include #include #include using namespace std; using namespace mediascanner; class MetadataExtractorTest : public ::testing::Test { protected: MetadataExtractorTest() { } virtual ~MetadataExtractorTest() { } virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(MetadataExtractorTest, init) { MetadataExtractor extractor; } TEST_F(MetadataExtractorTest, detect_audio) { MetadataExtractor e; string testfile = SOURCE_DIR "/media/testfile.ogg"; DetectedFile d = e.detect(testfile); EXPECT_NE(d.etag, ""); EXPECT_EQ(d.content_type, "audio/ogg"); EXPECT_EQ(d.type, AudioMedia); } TEST_F(MetadataExtractorTest, detect_video) { MetadataExtractor e; string testfile = SOURCE_DIR "/media/testvideo_480p.ogv"; DetectedFile d = e.detect(testfile); EXPECT_NE(d.etag, ""); EXPECT_EQ(d.content_type, "video/ogg"); EXPECT_EQ(d.type, VideoMedia); } TEST_F(MetadataExtractorTest, detect_notmedia) { MetadataExtractor e; string testfile = SOURCE_DIR "/CMakeLists.txt"; EXPECT_THROW(e.detect(testfile), runtime_error); } TEST_F(MetadataExtractorTest, extract) { MetadataExtractor e; string testfile = SOURCE_DIR "/media/testfile.ogg"; MediaFile file = e.extract(e.detect(testfile)); EXPECT_EQ(file.getType(), AudioMedia); EXPECT_EQ(file.getTitle(), "track1"); EXPECT_EQ(file.getAuthor(), "artist1"); EXPECT_EQ(file.getAlbum(), "album1"); EXPECT_EQ(file.getDate(), "2013"); EXPECT_EQ(file.getTrackNumber(), 1); EXPECT_EQ(file.getDuration(), 5); } TEST_F(MetadataExtractorTest, extract_video) { MetadataExtractor e; MediaFile file = e.extract(e.detect(SOURCE_DIR "/media/testvideo_480p.ogv")); EXPECT_EQ(file.getType(), VideoMedia); EXPECT_EQ(file.getDuration(), 1); file = e.extract(e.detect(SOURCE_DIR "/media/testvideo_720p.ogv")); EXPECT_EQ(file.getType(), VideoMedia); EXPECT_EQ(file.getDuration(), 1); file = e.extract(e.detect(SOURCE_DIR "/media/testvideo_1080p.ogv")); EXPECT_EQ(file.getType(), VideoMedia); EXPECT_EQ(file.getDuration(), 1); } int main(int argc, char **argv) { gst_init (&argc, &argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } mediascanner2-0.100+14.04.20140403/test/media/0000755000015301777760000000000012317231021020646 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/test/media/testvideo_480p.ogv0000644000015301777760000001524512317230414024160 0ustar pbusernogroup00000000000000OggS ESV*theora6V (OggS EA :?theora+Xiph.Org libtheora 1.1 20090822 (Thusnelda)theora(kIJs1R!1b!@d.UIvpk@HGәd0K%R,F"d1b0F!`, h0֕UTёPO ̌KKJ LJFFFE‚AAAAAA@!31pSa5u!bSFtт3tvwT'Fv1!6661!Q&666166662&66666666666666666666666666666666666661AAAA 3Bꉽf"G?r"^}jfWbB`K0ՆU)^}4̮؂ȣU62^?9w1+< sTWcAiAuܐek1x^ yh"cuk3)5 @ x]w(=ιM+F_Ţ9"ͯb%r({IzJy'Jl@CݺcylZu+1eG#FidR,>id<t +-1R܄֏^vj"|v}-VK&b'w@k^KYj#8UIJβbQ#9C6W8bǒ/j>FaП2[ E(,y?mB"\t-2{|H纊V/>mD<#H2nm0@u ]P9;DzJi## W`CӸs548r&yF \{Icp3%ΪS{,50r=2\Ta}{ n5NaVDK7N4 n5&6dEc}BF\g 7b.خ <)#wVQ!f (mK \N1f2Biv}jD8˨*R&spZ~z.+D)p=d:0 ЫZ4]%M܃&C:ŔȹӰY=-|[יŕ4tKсMFpZ-_ [Map#r,Y>e*C , S6M#ϓh8}u*YS6Ocx;D?mp71qZq1͵L_Yb pErr2jrEYb{FV fm,& )EԜ}7 _s yZhmR&H7y߀(nxH&gݱwT)|1$jD-:?0+OݠoRp[D%fڥi6LpÔ#YXޥ(x/`ŐT3`]AoռX*`6fr{D(zG+#wʔ~ϤVVF ə4́Qӥg!t 4Ͷ[슠)wbB& GQ8?,"$%m6jR\e߁&I*/?uܶd<*#$%4{8;ezq=+~`SPwfi3meaZ}bd6ʣMDVXOۋx?OĪ'YXg wqu6ژP4' U"'ְ JB*!9U /\eL屃xSA@jg@n"^ui-36s tqaI~BAd@883ȶu&|kQߥU#x!p: pglLGrK+y*FCt"ܙG~VUT8Ox&i#!eb_z.쎏R_, FS4Yot*V8$/J4Ͷ̱ѭ,k#p!:?JL晶"SQI@z;qϡ]7i3 ͵^ '+R2Z583^]QdQX^Lsbm iJ@F,qdD'6,Zf]k 䐀OggSE EKH? L{}{}{}{}x{}{}{}}x{}{}{}{}{}{}{}{}{}{}{}{}>{}{}{}~Z>jT>UC|CҨ}j PWAUJWC_T>1T=*> =_|bzUa|KONiW PDžP=*xxǥ_|5CCx}¨}~xU}ү> zUT>T>O@4h4A ( *(*( *( (** (**`Af@B48, AC da!m4?р@B@F9f0ŶhV-0 #@`,8m[Ft|` A0m[|hp   r`mр [`@A p !@@` A#@fm`+omA  d0`h [mem-:>0  r`mр [`@AF8 0 Y3 qm0| `,8m[Ft|204a-F2oр@B@F9f0ŶhV-0 #@`,8m[Ft|` A0m[|hp   r`mр [`Hc驰Dm M əh-k/kș h ^mv`:_I)4$C X"6fmhZ Z Avș B׶ZK)%)2q,aZֻB]&@d-햽lJIJLD2#l@`K&fVдl d-{e[RR g `əh-k-"d&B ^k/ԤC X"6fmhZ Z Avș B׶ZK)%)2q,aZֻB]&@d-햽lJIJL,2n,pB] k)I'TdD[#l)@e?OdZ[]LlEl\2" d(Vl@"ֶ. 2RddZZ ggk[ )JU?탲[-i-i3툵DBd *-?Z"!2@ Rl`ZKZd b-kb )@ GvKe%2ۅpȶBo?Ů*a8`oeXaeXaoeXaeXaoe`,,,07,0,0{, , oc{, , oc{,YeYeYa71YaYa7eYec|ceYec|ce0,07,0 11 4z=k5BtA|.!Y]4z=k5BtA|.!Y]4z=k5BtA|.!Y]4z=k5BtA|.!Y]4z=k5BtA|.!Y]4z=k5BtA|.!Y]4z=k5BtA|.!Y]4z7DԾA?thZ+.Z-""hWK~]_whZ+DEEh7ǢhWHE]/u~oߏEhME_ߛE]"".-Et77~=EDD]4Z-~o˫~nz-EtEu~oˮ=H"D$[lD+H"D+H"D$H"D+H"D+H"D$H"DH"EH"D$H"D+H"D+H"D$H"D+H"D+HlD$[lD+H"D+H"D$H"D+H"D+H"D$H"D$H"D$hѣD$H"D$H"D$hѣD$H"D$H"D$hѣD$H"D$H"D$hѣD$H"D$H"D$hѣD$H"D$H"D$hѣD$H"D$H"D$hѣD$H"D$HD+$H#FD$HF"D$HѣFh"EtD$hѣ4H""D4hњ$H]"D4h$H"D4f$ODE1k,X1bBƱbŃ!J(P1k,X1bBƱbŃ!J(P1k,X1bBƱbŃ!J(P1k,X1bBŃA$IeY$IeY$IeY$IeY$IeY$IeY$IeY$ZI$HUfe2wvWwyffeI]fS)wi%wwLff]ݤfe2wvWwyffeI]fS)wi]S3.3! 3330B333! 3330B333! 3330B333! 330B̦H@EEEFGGGDEEEDEEEFGGGDEEEDEEEFGGGDEEEDEEEFGGGDEEEDEEEFGGGDEEEDEEEFGGGDEEEDEEEFGGGDEEEDEFGDEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGl{DZIl{DZIl{DZIl{DZIl{DZIl{DZIl{DZIl{Il;ðض-b1;NӰ;cbض-Ӵ;Nð; bض#NӴ;ð&6-b،m;NӴ;ðض-b1;NӰ;cbض-Ӵ;Nð; bض#NӴ;bӴ;NӴ[űl;ð;NӴ[űl;ð;NӴ[űl;ð;NӴ[űl;ð;NӴ[űl;ð;NӴ[űl;ð;NӴ[űl;ð;Nűl; I$I$I$I$I$I$I$6mmmmmmmZ@J?yoQ%)NvSP( @P( C 4hѣF4hѣYOggSK E<mediascanner2-0.100+14.04.20140403/test/media/testfile.ogg0000644000015301777760000003474712317230414023207 0ustar pbusernogroup00000000000000OggSF-vorbisDwOggSFIHvorbis-Xiph.Org libVorbis I 20101101 (Schaufenugget)GENRE=Progressive Rock TRACKNUMBER=1 ALBUM=album1 TITLE=track1 DATE=2013COMMENTS=This is a commentARTIST=artist1vorbis)BCV1L ŀАU`$)fI)(yHI)0c1c1c 4d( Ij9g'r9iN8 Q9 &cnkn)% Y@H!RH!b!b!r!r * 2 L2餓N:騣:(B -JL1Vc]|s9s9s BCV BdB!R)r 2ȀАU GI˱$O,Q53ESTMUUUUu]Wvevuv}Y[}Y[؅]aaaa}}} 4d #9)"9d ")Ifjihm˲,˲ iiiiiiifYeYeYeYeYeYeYeYeYeYeYeYeY@h*@@qq$ER$r, Y@R,r4Gs4s=:<;<=:H14&,H;<<767:<=L.H=;> JF!* Yigf-?%#EQEccdzG F\\2/ojدt`~-oۮTW*9PmEt.g,4om"" <-Gvw<$OMb9T|d3AN`Vp:e"s > N \+G~5zPZX>׿XO > N ]5W\&j X_j`Ͼ>t?~{-7> Nl#+I.~M \鶠V X> N\W,h/@?wj~ =b]2́?> N \w(&PeNvXs{Yl3FmKT> N \w(&PeUX r֋ N`]AI J:w%ƝD'<cŖ> Nϋ`\+C2* o^#WxrԽ`Jo\ "RHB]߹3tj@V?%v[lꪪXEQe5MMM}DW>ra m0^D߹[ ~SuwvfT\ymW,1<56r` `<`I-LW \K30-W6@/E+$韍km1nɲM5^$\l&a8k/O)'QJow]\5&-\^u.Xmn#<4yW%:sl/y#!pd ._R/c$=E)c_ ?] `~>{r;g9> PF/Bzژ~{KwÆכI.> ._R/c$=Ec_ пSfW~K/z7> .ϗVIO1~ p%}9"2qȹ; > .pM%=:FUPھxku'j> .0R/cGz~UuV5r\;P9y> .M$=#[Uȕ_^:6H&> .11/cy^:; `ME4> %RpaJyr6u}Ψky v=#{r4b^ $G, cpVxg[IDG=?[?P5pAC64Gl@ew@z&Tg>?{~zDg=6P]o\TG>;_j7gDG=6z2[ M֡#D[z7ba jalbؖAYUܱ[nyύ?TxbWZT٭T @4Ü9[VuE)CVwu]Co=ds= vGi`u?S"A/4s>c>zI|bßFk c1XgJ\y.uy+*Mrh[$ NUgH?~eX.<q> Nw6_4 jZ+JWe e> Nw6_4 ~x[5 Ʒ˶s'- V=> Nw&'/Ӣ<p?eXR7ta fk@6> Nwhdj_@ T0!UJsuvMp > Nw&_$Ӣ >?M\.o_n:C Eum> Nw&_$Ӣ ]pVd,92*bg> NwH/ 5P)J&<&Ug> Nw_$uznm'ൎ U E{t>64h8_੔>0L6./;o^|n}1S_x wy2Q?'fJIvf϶EU C6ge2_O4lEXs w1L3"'x]G5u68\ELN\<;f#ٮ:V.iסtN2l~w#=fT;)MzEK[>޽; =~!>(e\~7]4Tf˅,$> NUgH/ 0k m--@5A> NUgI/ 0hY:x "+oz&M > Nw`dh ,* L> NwψPФJXeh~`> NwH/Z 4 |eȪXk`i > NwH/F~x@omzu`V*> NwhdhZaE] Ѽ> Nw&_*/-wWT)̷|zۮ5> NUgH/ @?O?۾ۛTהf=5M{[[>ZXloa6:ԞCwcq{9z-?w_gZr(º D\1ϝW`\Qk),Gg۳ᅱmCz $/>DQw1[>0AdbKգv4K {k?^|ci"&&k9fMKi;9ܓy]-VSԭMdo/LA^|D2z ={SuTȷW"^מDvDlewK3hS$VF_Ojho޼9 ?Дlgl6G/(> Nw_4 ?}SM[0.g| Z .> Nw_~ Pehfj#@> Nw_ h~w@(O R%L֢׉OggSFdDc<<=:97M1#K<8=>:==::<=:=F1P==7779=9=L/L;=:==;:85> NUg3_$C`@?Ϟ{3۷}[9S- <+> NUgH/ 0S "}$f{$Y> NwM=HȠ*в0!UJG*zm> NwH/Тq@"Nh] L> Nw%_$C Фu@ȪX?> NwI/Z 4  ~X@0~l ou]J[ _^P9?|L7ۙ5sΑ%Hb[R 3:'5#\l{`_]ՍZ+ ˟wI࿯_$HUt}/xFXX2}L3E]?krS6_d~֦64/?Ӕz=L4djs[UOx/XY%ԳS:M$O$?I-g$j?SͲ@\><9$:.Bʶ_ M-OUpx g 8*KlLCT>:"V> NwM=H/ȼh`*SJLx6u,:> NwIȲh_ BaIf%d (4Re2> Nw&_4Ӣ ~Z"+JG!4B > Nw6_4 H?n1.[ > Nw&'/0 ~@XefC:V|-;> Nw_$C S [Z ,> NUg;_>/~t}۷E g> NUg3Qm?'^Cdf@/>&> NwI/Ƞ*@? 0!UJs.fiuMryu?:/@ oFH,+-k{cT"|%6leݿ^q-phu6FҺV?_y'ku鋼֏|= \qw` }KJ) Nw_PP)Rn&J`&_/> Nw_>/@ i^k;|{S8 N> Nw&_$  uY1K6>B<> NwM=!'/SE4 ׯuL*̕)r> Nw&_i` %ݞ %B}> Nw&_$ˢ ~5Ȋ:*\> NUg i~~IU۷~Z: > Nw&/PH? a4]k@ȥ/> NI߯AE4 X Ȋ敽u@tvMrb~&|>k/9l~]6uQ$o@kZc_.{="X*CZk5?g] wzӇuAÐtdUa\| 4),.JcSLM1)g)τ255ajjj g7&SSS~ Nw&_*/Pec֟CD]X+17U> Nw6_*/Peq@L@0W?> NwH/P<BaI=P?`PԱ > NwI/FT`}<LHR\] @0n> NwH/F(ii۾×)&j> NUgH/F]XXȨkY> NwM=H/Ƞk*pk>o1> Nw&_Pm?d 􀒴X*)> Nwe }H>/~>~ "+JGo?o5MREh/U`TۗCa_s> Yy^QX]+ׄ8U>gQQkr෩VnlS5y̥L(;_&f%[2}Z$ͧ& <L||ӔL6_(yiZ L=)Yn ?'s>?  Aw-O SPNmjoQigWR|rw#XPm{8ym3"f UJy?~z޽; C ~OxO=?V}~כ2wwwwwww> NwϦ_>/~&x{Jk Y[t֓> Nw'>/3`}x\X_v`B f3j`_> NwQm? PehKus> Nw6_*/@8A(]S> Nw6_*/Pec%֟@0LׇWi3K > NwHPEx;aIR,`9:4 > NwH/FT.>&R:on @0(Z> NUgcPTtfmMVL:> NwI/F^ ]5n)uOggST]FoVn1K/&H:<<<<:==9C(,&P<:>;><<<=Ngu]{ -}$]g辿Fx7Czd.(XRZk S`Zk|(k{Ʈ-|́aH/'v^rZstq6[Wnġ\FT6Zkq:-Qʴ@=LɯgZ0Vxa aJ~=MkVw uN-)~Ɣz.S3MMg02gmgw_43$=_YR~/ %|ƞ6=N'?9;τD/bͿt 2eF`[. MW7yՏGk^]$wYZ\m8r3> NwH/p? N@4 j> Nw`d^_@g@z0 SJLx@]> Nw&_$ 0p?||`oEo> Nw&_$ 0p?=%~M ȊD$/ > Nw'WPZ0!UJ̲ L}> NUg3_Pm?g6 O|`t{> NwϦ_>/@X:"+;UsW> NUgW} XNm߄W fE> Nw&P"ʰ KXwHM`V]UvMrxv!Q?? }X-- e2b&#z9& >U]uT;9^(Z+@Ϭ_<D;S#iNW|:s^xqqq->=Lpnvm! I<̳K_ϋߟf́s9眫\ a\[AZӭ+"[0 a]=b _ Ky : a\-e~5/Ͼ>Y]YD? a9m 5li wURKf a\-e~} >obvEr Pg al- 0@.?Z] ; `a\-> ү XVV^~]ziN~2!] ϗa9]H epƒYaTU@H ϗa9]-! e/fJ5 ٧V)i㖇]Qb _= CmUߧ&2ꮱNX!?cAuU(D_M׶_WFnaVk 4{gN_?oP_Ϸ֚T[g?3[1h#qM*a=^rOd Smediascanner2-0.100+14.04.20140403/test/media/fake_root/0000755000015301777760000000000012317231021022617 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/test/media/fake_root/bin/0000755000015301777760000000000012317231021023367 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/test/media/fake_root/do_not_find_me.ogg0000644000015301777760000003226212317230414026272 0ustar pbusernogroup00000000000000OggS2WJ?vorbisDwOggS2Wvorbis-Xiph.Org libVorbis I 20101101 (Schaufenugget)TRACKNUMBER=100 ARTIST=DJ JP DATE=2014GENRE=ElectronicTITLE=Goin' upvorbis)BCV1L ŀАU`$)fI)(yHI)0c1c1c 4d( Ij9g'r9iN8 Q9 &cnkn)% Y@H!RH!b!b!r!r * 2 L2餓N:騣:(B -JL1Vc]|s9s9s BCV BdB!R)r 2ȀАU GI˱$O,Q53ESTMUUUUu]Wvevuv}Y[}Y[؅]aaaa}}} 4d #9)"9d ")Ifjihm˲,˲ iiiiiiifYeYeYeYeYeYeYeYeYeYeYeYeY@h*@@qq$ER$r, Y@R,r4Gs4s/ Sj,fu^`Y3/)}a @Kl5 I^߀@u-@0_RY^—YڰTw۾#g<i2oɽ4a<Rx#vi2o)4`\CRT1uvlY^2/ag}[w.۾y:mI^Tv L)Yçj9qv@,mA_Y^3iyDVd<' YO=~: J5UYO)=ĿKloms_Y2O)-$8=uxtI3O>s}`mP*EܷI^‡TT"`| Y2/ @LUI]|G=Yps2`( c9n؍YÇT "Wx^IOi9{þ !]eE?oI×Ԯ**r*soIç>BþP7?t%Yg&YdTB`3 ׍.8YR S'ԍtw[ EITWBaa_0@G|/IU޵aa_ gɯI ]Խ&"ʨYdW xRX p txI|Y&$ާ ws^[H I՗dU>a@Q] O~2I!$޵¾Ȋ i-JIU -O 8H`|+EUmIU _pr/hSRtńIZpo)UUIU_p갰/ʪ3^MI[T+o1)J&4>X9]":]IC .?w7{@0L5Idg7SK OWIdT/]RݣҾAICqO=o@XɔY_~9C0{8},u^$IC2.}`5@矛SInSQ>yho 0!ݛ*MInSQs=@uҽU)zIKRr 0  пRݍTq~9n[`f?7p`49[`~U)&5In[b;S RW}:*In[/*) BUCUE2)In[`~ Z@60BTد~9n[ ~i`B:"!~9nJ`+PKRHv In[*}Sj,JI[FwY*Z&z*ViLIn[k? O`B3Q~9{y?XR|2ow^)_ ̣& 0~9˗?_gh?IZg@9U]ge{?9@? THV|ٝv ~9?@=~\M~=yky^).kã_}w~9? . ,J+e lW97H:c?ib/vʟK,9׷. ҩ^z >~9?@f qOe篻=^)/? ^Pq߼T/g^)/ ߮a^'?gz(~9/?.0 ˆȈ0mK(~9Ǘ?[%@ 4\aqx~9/?@Vk*^)/?^`Ϳ\\l^)/?_`O0$9`X~)/]aүߙyRu0~9/?`_BXbi`_V9N7}9!-~9/?/[!,xKؚ%^)n/?_.W]<[ZAw^n/!Wk^-^)n/?_FBxXz1{~)/?e=/קR9~)/?e  eu@ŗN7^)/?/s8~륦KL-,^n/`oN5CKʒ^nOo~hߡ︫ : ^)/`/8[%`qQIDkJ~)/`Q "o0( {E)^)//^G/ NN Τ^n/`/~:@ix-m^n/`!Bx dSdC$^)/# /X>??7?s ^)n/`a~=G_`?ߟ.Ng^)n/a~8ʗ_OggS2W#6b,',-.,&,---'*...+),.-+',-,+$*//,(),--*(,,+'$++,*%*+-+&(,++*&*,,*$)++*')++,)'-+,*&+)))#'(''&%,)+'$+^nO~Fd j:叆(>/``^py=X?0^/ {Y @P!vP*ލ^)/`a~(H [}~^nO$H aO7۟{|>ORx$'# _z XC)M8 ./?)ao#@Wwh>./`d CRThJ^/j^GHg@ߧ3^/r^ ퟟ>2=c~ >./dԳ N%O ./ZF}#WG > ./G= @Lo l>Nor~2H[gT>./4f w{B>./Bޏ:_hr|*ߵ` N/C@;-a< NoC3G}p0՝$#SY]/>NoCF]S[xO>N/C0&6@`{o?/l>./CvY"_Zmߜ[ 'ڤ N/?(3Ǽ _0}U^Oâ7A@Rw*/&F  /CYl _`5rItL/ӗS> NOaNKDAj&X!I!S_!> NO8K aˇc'R|h /? =G I@.GqqJO? [T9P|T/?Q)=y^ O?{Q N7Pm7p-[% O?8s Hszm?3 /?!ʿU/ 'ˇxO~vO?z7nѯX߰88$.~HM/?r7k 0~نJo?÷|oqD=WT(mO?eolA@ yzvz7mO?eolxړO/kWl^?7|7@@*J> ?c28",I傓?e2b{RTH]V{?7|7` @*g|_} oA 4zgLOs9S\["o?3rGh ]3WMmoB8Sj@BP 9$mOJY  z0L0ɪz譏OJY =/*+;<mOCLEwB '_18}L:$md@*+fEMK[حOCyjp7^JR}3GjȭOH^N pH!yحOp ^nS6OC|D@Rt%LIg حOC$xDB|-56ULHحO`|Da**.Eܓ p -O?R.,# ! ,a %-O`x4q]>e-ORK#FU h~[_\JT0a3-O`xDJSo:0jȭT h>"UjNW-O /oQ_ &%b-Oc„T &0MF-//Z  G&WYUFI-Olc 9RH\-O?ºc82u̒V: -ORV- s !@W& &n+-5|Q\B )"e7 -O𗉷(w ! Zv9koz }ȭ_F.8b;@XR'LH}opd-O@o1;Ȕ3=X ~-F8X@CХo{B y=~-Ϗ([v !vL0X~Mu|- 1pJU mkJ-Ϗ- 0Ch|)))_=^p-ϏT/- 1brzMwʩj+Hu~-w+EzCC)BP )d<^-JeNz;_^Mg#= K( ~MEzz -SN9S̶6~Mwr"=E!48Ur)q4u^78S, 4<8@Sos>^-WJ(BB )7^(`s2\rPyٺ^7B8X/paB &{0^7V)oQ@X/Ƌ:^7 (G.LHu+ &>a5- Bjl@m>WWPޢ!_8;= >a5G[0  ):!+OggST]2W`+'($),,(&(*,)&')+A> ioQ@k"~)۾x >WHoQ@g)rS{mDG|E HH!JRHl7wǧW&EJ/ jׅWRޢFE%@b:)BP );z>W|.:b(W׸PޢE%>%}:R~*x5l)_Q@r}JH!RTHy$g۷iTOro> Z'w74ϊJB ) )&wWhԸ&}E%@ooTw5]CJϩo|ϔ3e; n\gWի"}A#۷}۷nmgiTzE"!|W뷫Wr/&ؾ .NgW=j&b[4Bp &`"+`Jg˷<|&//LdEV" \?WugQfX/DpT.ګχ^^heFN*+.>sͭk|'c?wNUEs1_Tlhd)-⿵ ?MomK /*/omj/mj/mjM- hjES{"?S{"?S6_`$mediascanner2-0.100+14.04.20140403/test/media/fake_root/usr/0000755000015301777760000000000012317231021023430 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/test/media/fake_root/var/0000755000015301777760000000000012317231021023407 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/test/media/testvideo_720p.ogv0000644000015301777760000002227712317230414024160 0ustar pbusernogroup00000000000000OggS 1DHū*theoraP- (OggS 1 :?theora+Xiph.Org libtheora 1.1 20090822 (Thusnelda)theora(kIJs1R!1b!@d.UIvpk@HGәd0K%R,F"d1b0F!`, h0֕UTёPO ̌KKJ LJFFFE‚AAAAAA@!31pSa5u!bSFtт3tvwT'Fv1!6661!Q&666166662&66666666666666666666666666666666666661AAAA 3Bꉽf"G?r"^}jfWbB`K0ՆU)^}4̮؂ȣU62^?9w1+< sTWcAiAuܐek1x^ yh"cuk3)5 @ x]w(=ιM+F_Ţ9"ͯb%r({IzJy'Jl@CݺcylZu+1eG#FidR,>id<t +-1R܄֏^vj"|v}-VK&b'w@k^KYj#8UIJβbQ#9C6W8bǒ/j>FaП2[ E(,y?mB"\t-2{|H纊V/>mD<#H2nm0@u ]P9;DzJi## W`CӸs548r&yF \{Icp3%ΪS{,50r=2\Ta}{ n5NaVDK7N4 n5&6dEc}BF\g 7b.خ <)#wVQ!f (mK \N1f2Biv}jD8˨*R&spZ~z.+D)p=d:0 ЫZ4]%M܃&C:ŔȹӰY=-|[יŕ4tKсMFpZ-_ [Map#r,Y>e*C , S6M#ϓh8}u*YS6Ocx;D?mp71qZq1͵L_Yb pErr2jrEYb{FV fm,& )EԜ}7 _s yZhmR&H7y߀(nxH&gݱwT)|1$jD-:?0+OݠoRp[D%fڥi6LpÔ#YXޥ(x/`ŐT3`]AoռX*`6fr{D(zG+#wʔ~ϤVVF ə4́Qӥg!t 4Ͷ[슠)wbB& GQ8?,"$%m6jR\e߁&I*/?uܶd<*#$%4{8;ezq=+~`SPwfi3meaZ}bd6ʣMDVXOۋx?OĪ'YXg wqu6ژP4' U"'ְ JB*!9U /\eL屃xSA@jg@n"^ui-36s tqaI~BAd@883ȶu&|kQߥU#x!p: pglLGrK+y*FCt"ܙG~VUT8Ox&i#!eb_z.쎏R_, FS4Yot*V8$/J4Ͷ̱ѭ,k#p!:?JL晶"SQI@z;qϡ]7i3 ͵^ '+R2Z583^]QdQX^Lsbm iJ@F,qdD'6,Zf]k 䐀OggSC 1EV0 L{},>{}{};}y}y{}{}{}{}'xϿ>Ͽ>{}{}{}{}{}y}y{}{}{}{}{|Ͽ>Ͽ>{}{}}{}{}}y}y}{}{}x}{}{}w>Ͽ>Ͽ{}{}{{}{}N}y}y{}{}{y{}{{}T>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}SxT>eP¨}Sv:}Ҩ}U}߼}P8xǑx>:qT>#~|uCx}Fx>P<>}㊡y}xCx7xT>*>o>}U}߼}P8xǑx>:qT>#~|CO{} 4{}pO{} 4{}pO{} 4{}pO{} ***********@ (  (  (  (  (  ([! )@C R03yG0ڛ $-a*@ !@@`<TfofaFQ` r80p`FffJm``[l F0QfRmFр*@ !@@`<TfofaFQ` r80p`FffJm``[l F0QfRmFр*@ !@@`<TfofaFQ` r80p`FffJm``[l F0QfRmFр*@ !@@`<TfofaFQ` r80p`FffJm``[l F0QfRmFр*@ !@@`<TfofaFQ` r80p`FffJm``[l F0QfRmFр*@ !@@`<TfofaFQ` r80p`FffJm``[l F0QfRmFр*@ !@@`<TfofaFQ` r80p`FffJm``[l F0QfRmFр` @ o`m-LDXLZ@hI( X:K)%&&צ@4`) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l) "-)VrdK)%)2KZK^l[AVuKA2Og2[-k5Y!h2%ri"(5!AKLZZd ,J͔L3u]rdKadle@͔LS7\&@LFAKLZZd ,J͔L3u]rdKadle@͔LS7\&@LFAKLZZd ,J͔L3u]rdKadle@͔LS7\&@LFAKLZZd ,J͔L3u]rdKadle@͔LS7\&@LFAKLZZd ,J͔L3u]rdKadle@͔LS7\&@LF@sn۩{uݒ&! iB"Fm!D""$BD" $LD"DD"$BD" UUDH!D"5iHD" DH!D"6hB!D($j&ӈ0!D"5hB!D(TmV B!PHM `B!D($j& B!P ڭ"@!B!N"@B!PHM"@!B!Q[DDBB!@#Q6D B!DDBB!@*j"B"Fm8B!@#Q6D(MwE/^jպV7At |z;5j+Vwt _z=zKךn;|oA]/_z={ZJ՝7 .|/=Gt^[j|nAKG/^zVҵg|o7B ~_ GtyVZ7E?|/G׺^իtY|oЂ"|?wE/^jպV7At |z;5j+Vwt _z=zKךn;|oA]/_f՝2wuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6LwuJJTƙ6L(PB hѣP (PB4h @ B 4hѨCA (SAB 4j @B (PF$(P`5h @B h(XF@(PB hѣP ,ѣF (PM 4hѨ@ (PB 4j @B4h! )`5 @B (XF@(PF4 PB4,ѣF(PB 4hѨ@@ hѣP (P4h @ (P`5 HPB 4j@B PF PBha"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$WDH""D$H]"D$H"EtD+$H"D$H"D$WDH""D$H]"D$H"EtD+$H"D$H"D$WDH""D$H]"D$H"EtD+$H"D4HÇ8pÇÇ8pÇw8pÇ8y8pÇ8ppÇ8pÇÇ8pÇw8pÇ8y8pÄB  (PB`B  (PB`B  (PB`B  (PB`B  (PB`B  (PB`B  (PB`B  (PB`B  (PB`B  (PB`B  (PB`DXmRJ9mRJ9mRJ9mRJ9mRJ9mRJ9mRJ9mRJ9mRJ9mRJ9mRJ9N` 33 `33: 33 `33: 33 `33: 33 grI]f$fffbI]f$fffbI]f$fffbI]f$fffbI]f$fffbI]f$fffbI]f$fffbI]f$fffbI]f$fffbI]f$fffbI]f$fffbYwywywywywywywywywywywyo>}@>|ϟP>}@>|ϟP>}@>|ϟP>}@>|ϟP>}@>|ϟP>}@>|ؘؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdᓆN8ؘؘdbc[DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@=c0I$cc=I$c3 $F6=c$I=c0I$cc=I$c3 $F6=c$I=c0I$cc=I$c3 $F68I$I$I$I$I$I%@4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @4 @;NӴ%(!mmmmmhZJ?7_ |/_ ~Nw'q;NwTOggSJ 1bOggSK 1&Dmediascanner2-0.100+14.04.20140403/test/media/testvideo_1080p.ogv0000644000015301777760000003153612317230414024236 0ustar pbusernogroup00000000000000OggS)^KiX*theoraxD8 (OggS)^x :?theora+Xiph.Org libtheora 1.1 20090822 (Thusnelda)theora(kIJs1R!1b!@d.UIvpk@HGәd0K%R,F"d1b0F!`, h0֕UTёPO ̌KKJ LJFFFE‚AAAAAA@!31pSa5u!bSFtт3tvwT'Fv1!6661!Q&666166662&66666666666666666666666666666666666661AAAA 3Bꉽf"G?r"^}jfWbB`K0ՆU)^}4̮؂ȣU62^?9w1+< sTWcAiAuܐek1x^ yh"cuk3)5 @ x]w(=ιM+F_Ţ9"ͯb%r({IzJy'Jl@CݺcylZu+1eG#FidR,>id<t +-1R܄֏^vj"|v}-VK&b'w@k^KYj#8UIJβbQ#9C6W8bǒ/j>FaП2[ E(,y?mB"\t-2{|H纊V/>mD<#H2nm0@u ]P9;DzJi## W`CӸs548r&yF \{Icp3%ΪS{,50r=2\Ta}{ n5NaVDK7N4 n5&6dEc}BF\g 7b.خ <)#wVQ!f (mK \N1f2Biv}jD8˨*R&spZ~z.+D)p=d:0 ЫZ4]%M܃&C:ŔȹӰY=-|[יŕ4tKсMFpZ-_ [Map#r,Y>e*C , S6M#ϓh8}u*YS6Ocx;D?mp71qZq1͵L_Yb pErr2jrEYb{FV fm,& )EԜ}7 _s yZhmR&H7y߀(nxH&gݱwT)|1$jD-:?0+OݠoRp[D%fڥi6LpÔ#YXޥ(x/`ŐT3`]AoռX*`6fr{D(zG+#wʔ~ϤVVF ə4́Qӥg!t 4Ͷ[슠)wbB& GQ8?,"$%m6jR\e߁&I*/?uܶd<*#$%4{8;ezq=+~`SPwfi3meaZ}bd6ʣMDVXOۋx?OĪ'YXg wqu6ژP4' U"'ְ JB*!9U /\eL屃xSA@jg@n"^ui-36s tqaI~BAd@883ȶu&|kQߥU#x!p: pglLGrK+y*FCt"ܙG~VUT8Ox&i#!eb_z.쎏R_, FS4Yot*V8$/J4Ͷ̱ѭ,k#p!:?JL晶"SQI@z;qϡ]7i3 ͵^ '+R2Z583^]QdQX^Lsbm iJ@F,qdD'6,Zf]k 䐀OggSC)^k,% L{}{}x{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}{}{}vx{}o}5C5PWIx*>j}x>xMT>U}PT>o>J}x*>j>~U|CxT>U|5~}x> }߼|C5P>U|C _}&x>}xj7%PT>|C5PWIx*>j}x>xMT>U}PT>o>J}x*>j>~U|CxT>U|5~}x> }߼|C5P>U|C _}&x>}xj7%PT>|C5PWIx*>j>J}߼}P>{}x>}ި}x7xT>}Cxǹx>:}xx=>}xǽP>o>}x=>s~|uCx>T>{}x>z}߼}P>{}x>}ި}x7xT>}Cxǹx>:}xx=>}xǽP>o>}x=>s~|uCx>T>{}x>z}߼}PC@P(h4  C@P(h4  C@P(h4  C@P(h4  C@QTPU@UEEQTQTPU@UEEQTQTPU@UEEQTQTPU@UEEQTQTPU@UEEQTQTPU@UEEQTQTPU@UEEQTQTPU@UEEQTQTPUHҨ*( *( *( *( *( *( *( *( +ce^APW1^0sAz{e+L:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tL:☯AꂡLW2b st+tVײZL4i:57"!2MfTl"d@hIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AIZZKdL@DX0ȈLԩS2&@dHE26*д%dL̀JdE J3*RdvKaS shZ R AI@kG uf͖L@&R s_@@L՛6Ye2LʔL͡hh d K @&@͛, ReJL_@дFд2?db F uf͖YLS2&@/ shZ#hZ21# :f,ATR s_9--L~YeS *T̩I9/KBLDdٲ)@*fTdm @Dm @S!_AF"a2Vle J3*RdK26"6)/ #06ld%J)2?~BB@@L՛6Ye2LʔL͡hh d K @&@͛, ReJL_@дFд2?db F uf͖YLS2&@/ shZ#hZ21# :f,ATR s_9--L~YeS *T̩I9/KBLDdٲ)@*fTdm @Dm @S!_AF"a2Vle J3*RdK26"6)/ #06ld%J)2?~BB &-o0F%@! ! 0@ PBBD!@!`"TB`*!PX0y(,<DJBB `"%@! ! 0@ PBBD!@!`"TB`*!PX0y(,<DJBB `"%@! ! 0@ PBBD!@!`"TB`*!PX0y(,<DJBB `"%@! ! 0@ PBBD!@!`"TB`*!PX0y(,<DJBB `"%@! ! 0@ jNZz;~[CtyA]5ZZQz=}oCn׺^ .SVz=}_t7E/^dAMV֫Tz=Gt[}_t:2 TU=G_Wt: zKיEUuG/W:{̂":jG}oCt^AtjjGwKC/^z jNZz;~[CtyA]5ZZQz=}oCn׺^ .SVz=}_t7E/^dAMV֫Tz=Gt[}_t:2 TU=G_Wt: zKיEUuG/W:{̂":jG}oCt^AtjjGwKC/^z jNZz;~[Ct}Cu~_wuJJTt:Z ߧOw]tMCuН~_wuJJTt:Z ߧOw]tMCuН~_wuJJTt:Z ߧOw]tMCuН~_wuJJTt:Z ߧOw]tMCuН~_wuJJTt:Z ߧOw]tMCuН~_wuJJTt:Z ߧOw]tMCuН~_wuJJTt:Z ߧOw]tMCuН~_wuJJTt:Z ߧOw]tMCuН~_wuJJT@#F  @#ѣFB  @~hѣPB H~?4hѨPB$~?4h(PB?M4j(P &5 (PF (@G F@  @#ѣFB  @~hѣPB H~?4hѨPB$~?4h(PB?M4j(P &5 (PF (@G F@  @#ѣFB  @~hѣPB H~?4hѨPB$~?4h(PB?M4j(P &5 (PF (@G F@  @#ѣFB  @~hѣPB H~?4hѨPB$~?4h(PB?M4j(P &5 (PF (@G F@  @#ѣFB $H"܉-"E$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$H"D$WMȑ""D$H]"D$H"EtD+$H"D$H"D$WDH""D$H]"D$H"EtD+$H"D$H"D$WDH""D$H]"D$H"EtD+$H"D$H"D$WDH""D$H]"D$H"EtD+$H"D$H"D$WDH""D008|Çp>aÇ8p08|Çp>aÇ8p08|Çp>aÇ8p08|Çp>aÇ8p0BeP*b"B,*Rm ",B)Km B,*Rm ",B)Km B,*Rm ",B)Km B,*Rm ",B)Km B,*Rm ",B)Km B,*Rm ",B)Km B,*Rm ",B)Km B,*Rm ",B)Km "RFAJRmA)mAJRmA)mAJRmA)mAJRmA)mAJRmA)mAJRmA)mAJRmA)mAJRmA)mCdPd                                                                 ̻YwwwvY˻˻n]f]ݤfcr32$3wywi%ܻ̻I,feIff7.3.K31wwwvY˻˻n]f]ݤfcr32$3wywi%ܻ̻I,feIff7.3.K31ww)JffffRfffe)JffffRfffe)JffffRfffe)JffffRfffe)JffffRfffe)JffffRfffe)JffffRfffe)JffffRfffe)JffϨ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|Ϩ_>|&&&&**(ƥbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbbbbbbcccccccbbbbcdllkr::::6665܎nGGGGFƷ#ccc[ѱttttlllkr::::6665܎nGGGGFƷ#ccc[ѱttttlllkeI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I&$=c4[DZeIl{DZ %{X$DZ{`[DZeIl{DZ %{X$DZ{`[DZeIl{DZ %{X$DZ{`[DZeIl{DZ %{X$DZ{J~hh~hhQTva1;cavð; avð&6aLl;ðva1;cavð; avð&6aLl;ðvavivivivivivivivivivivivivivivivivI$I$I$I$I$I$I$I$B2@OggSJ)^wy*tOggSK)^v?Rmediascanner2-0.100+14.04.20140403/test/test_sqliteutils.cc0000644000015301777760000000530312317230414023525 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include #include using namespace std; using namespace mediascanner; class SqliteTest : public ::testing::Test { public: SqliteTest() : db(NULL) { } virtual void SetUp() { int rc = sqlite3_open(":memory:", &db); if (rc != SQLITE_OK) { throw runtime_error(sqlite3_errstr(rc)); } } virtual void TearDown() { if (db != NULL) { int rc = sqlite3_close(db); if (rc != SQLITE_OK) { throw runtime_error(sqlite3_errstr(rc)); } db = NULL; } } sqlite3 *db; }; TEST_F(SqliteTest, Execute) { Statement stmt(db, "SELECT 1"); EXPECT_EQ(true, stmt.step()); stmt.finalize(); } TEST_F(SqliteTest, GetInt) { Statement stmt(db, "SELECT 42"); EXPECT_EQ(true, stmt.step()); EXPECT_EQ(42, stmt.getInt(0)); stmt.finalize(); } TEST_F(SqliteTest, GetText) { Statement stmt(db, "SELECT 'foo'"); EXPECT_EQ(true, stmt.step()); EXPECT_EQ("foo", stmt.getText(0)); stmt.finalize(); } TEST_F(SqliteTest, BindInt) { Statement stmt(db, "SELECT ?"); stmt.bind(1, 42); EXPECT_EQ(true, stmt.step()); EXPECT_EQ(42, stmt.getInt(0)); stmt.finalize(); } TEST_F(SqliteTest, BindText) { Statement stmt(db, "SELECT ?"); stmt.bind(1, "foo"); EXPECT_EQ(true, stmt.step()); EXPECT_EQ("foo", stmt.getText(0)); stmt.finalize(); } TEST_F(SqliteTest, Insert) { Statement create(db, "CREATE TABLE foo (id INT PRIMARY KEY)"); EXPECT_FALSE(create.step()); create.finalize(); Statement insert(db, "INSERT INTO foo(id) VALUES (?)"); insert.bind(1, 42); EXPECT_FALSE(insert.step()); insert.finalize(); Statement select(db, "SELECT id FROM foo"); EXPECT_TRUE(select.step()); EXPECT_EQ(42, select.getInt(0)); EXPECT_FALSE(select.step()); select.finalize(); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } mediascanner2-0.100+14.04.20140403/test/test_mfbuilder.cc0000644000015301777760000000576712317230414023132 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include #include #include"mediascanner/MediaFile.hh" #include"mediascanner/MediaFileBuilder.hh" using namespace mediascanner; class MFBTest : public ::testing::Test { protected: MFBTest() { } virtual ~MFBTest() { } virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(MFBTest, basic) { MediaType type(AudioMedia); std::string fname("abc"); std::string title("def"); std::string date("ghi"); std::string author("jkl"); std::string album("mno"); std::string album_artist("pqr"); std::string etag("stu"); std::string content_type("vwx"); int track_number = 13; int duration = 99; MediaFileBuilder b(fname); b.setType(type); ASSERT_THROW(b.setType(type), std::invalid_argument); b.setTitle(title); ASSERT_THROW(b.setTitle(fname), std::invalid_argument); b.setDate(date); ASSERT_THROW(b.setDate(date), std::invalid_argument); b.setAuthor(author); ASSERT_THROW(b.setAuthor(author), std::invalid_argument); b.setAlbum(album); ASSERT_THROW(b.setAlbum(album), std::invalid_argument); b.setAlbumArtist(album_artist); ASSERT_THROW(b.setAlbumArtist(album_artist), std::invalid_argument); b.setTrackNumber(track_number); ASSERT_THROW(b.setTrackNumber(track_number), std::invalid_argument); b.setDuration(duration); ASSERT_THROW(b.setDuration(duration), std::invalid_argument); b.setETag(etag); ASSERT_THROW(b.setETag(etag), std::invalid_argument); b.setContentType(content_type); ASSERT_THROW(b.setContentType(content_type), std::invalid_argument); // Now see if data survives a round trip. MediaFile mf = b.build(); ASSERT_EQ(mf.getType(), type); ASSERT_EQ(mf.getFileName(), fname); ASSERT_EQ(mf.getTitle(), title); ASSERT_EQ(mf.getDate(), date); ASSERT_EQ(mf.getAuthor(), author); ASSERT_EQ(mf.getAlbum(), album); ASSERT_EQ(mf.getAlbumArtist(), album_artist); ASSERT_EQ(mf.getTrackNumber(), track_number); ASSERT_EQ(mf.getDuration(), duration); ASSERT_EQ(mf.getETag(), etag); ASSERT_EQ(mf.getContentType(), content_type); MediaFileBuilder mfb2(mf); MediaFile mf2 = mfb2.build(); ASSERT_EQ(mf, mf2); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } mediascanner2-0.100+14.04.20140403/test/test_qml.cc0000644000015301777760000000500212317230414021730 0ustar pbusernogroup00000000000000#include #include #include #include #include using namespace mediascanner; class MediaStoreData { public: MediaStoreData() { db_path = "./mediascanner-cache.XXXXXX"; if (mkdtemp(const_cast(db_path.c_str())) == nullptr) { throw std::runtime_error("Could not create temporary directory"); } setenv("MEDIASCANNER_CACHEDIR", db_path.c_str(), true); populate(); } ~MediaStoreData() { if (system("rm -rf \"$MEDIASCANNER_CACHEDIR\"") == -1) { throw std::runtime_error("rm -rf failed"); } } void populate() { MediaStore store(MS_READ_WRITE); store.insert(MediaFile("/path/foo1.ogg", "audio/ogg", "etag", "Straight Through The Sun", "2013-11-15", "Spiderbait", "Spiderbait", "Spiderbait", 1, 235, AudioMedia)); store.insert(MediaFile("/path/foo2.ogg", "audio/ogg", "etag", "It's Beautiful", "2013-11-15", "Spiderbait", "Spiderbait", "Spiderbait", 2, 220, AudioMedia)); store.insert(MediaFile("/path/foo3.ogg", "audio/ogg", "etag", "Buy Me a Pony", "1996-10-04", "Spiderbait", "Ivy and the Big Apples", "Spiderbait", 3, 104, AudioMedia)); store.insert(MediaFile("/path/foo4.ogg", "audio/ogg", "etag", "Peaches & Cream", "2004-03-08", "The John Butler Trio", "Sunrise Over Sea", "The John Butler Trio", 2, 407, AudioMedia)); store.insert(MediaFile("/path/foo5.ogg", "audio/ogg", "etag", "Zebra", "2004-03-08", "The John Butler Trio", "Sunrise Over Sea", "The John Butler Trio", 10, 237, AudioMedia)); store.insert(MediaFile("/path/foo6.ogg", "audio/ogg", "etag", "Revolution", "2010-01-01", "The John Butler Trio", "April Uprising", "The John Butler Trio", 1, 305, AudioMedia)); store.insert(MediaFile("/path/foo7.ogg", "audio/ogg", "etag", "One Way Road", "2010-01-01", "The John Butler Trio", "April Uprising", "The John Butler Trio", 2, 185, AudioMedia)); } private: std::string db_path; }; MediaStoreData data; QUICK_TEST_MAIN(Mediascaner) mediascanner2-0.100+14.04.20140403/test/test_config.h.in0000644000015301777760000000014012317230414022651 0ustar pbusernogroup00000000000000#define TEST_DIR "${CMAKE_CURRENT_BINARY_DIR}" #define SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" mediascanner2-0.100+14.04.20140403/test/CMakeLists.txt0000644000015301777760000000345212317230414022340 0ustar pbusernogroup00000000000000if (NOT DEFINED GTEST_ROOT) set(GTEST_ROOT /usr/src/gtest) endif() set(GTEST_SRC_DIR "${GTEST_ROOT}/src") set(GTEST_INCLUDE_DIR ${GTEST_ROOT}) add_library(gtest STATIC ${GTEST_SRC_DIR}/gtest-all.cc ) set_target_properties(gtest PROPERTIES INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIR}) target_link_libraries(gtest ${CMAKE_THREAD_LIBS_INIT}) # Clang complains about unused private field 'pretty_' in gtest-internal-inl.h. if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-Wno-unused-private-field") endif() add_definitions(${MEDIASCANNER_CFLAGS} ${GST_CFLAGS}) include_directories(../src) configure_file(test_config.h.in test_config.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_executable(basic basic.cc) target_link_libraries(basic mediascanner scannerstuff gtest) add_test(basic basic) add_executable(test_mediastore test_mediastore.cc) target_link_libraries(test_mediastore mediascanner gtest) add_test(test_mediastore test_mediastore) add_executable(test_metadataextractor test_metadataextractor.cc) target_link_libraries(test_metadataextractor mediascanner scannerstuff gtest) add_test(test_metadataextractor test_metadataextractor) add_executable(test_sqliteutils test_sqliteutils.cc) target_link_libraries(test_sqliteutils gtest ${MEDIASCANNER_LIBRARIES}) add_test(test_sqliteutils test_sqliteutils) add_executable(test_mfbuilder test_mfbuilder.cc) target_link_libraries(test_mfbuilder gtest mediascanner) add_test(test_mfbuilder test_mfbuilder) add_executable(test_qml test_qml.cc) qt5_use_modules(test_qml QuickTest) target_link_libraries(test_qml mediascanner) add_test(test_qml test_qml -input ${CMAKE_CURRENT_SOURCE_DIR}/qml -import ${CMAKE_BINARY_DIR}/src/qml) set_tests_properties(test_qml PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal") mediascanner2-0.100+14.04.20140403/test/qml/0000755000015301777760000000000012317231021020360 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/test/qml/tst_mediastore.qml0000644000015301777760000000303012317230414024122 0ustar pbusernogroup00000000000000import QtQuick 2.0 import QtTest 1.0 import Ubuntu.MediaScanner 0.1 Item { id: root MediaStore { id: store } TestCase { name: "MediaStoreTests" function test_lookup() { var song = store.lookup("/unknown.ogg"); compare(song, null, "song == null"); song = store.lookup("/path/foo1.ogg"); verify(song !== null, "song != null"); var checkAttr = function (attr, value) { compare(song[attr], value, "song." + attr + " == \"" + value + "\""); }; checkAttr("filename", "/path/foo1.ogg"); checkAttr("uri", "file:///path/foo1.ogg"); checkAttr("contentType", "audio/ogg"); checkAttr("eTag", "etag"); checkAttr("title", "Straight Through The Sun"); checkAttr("author", "Spiderbait"); checkAttr("album", "Spiderbait"); checkAttr("albumArtist", "Spiderbait"); checkAttr("date", "2013-11-15"); checkAttr("trackNumber", 1); checkAttr("duration", 235); checkAttr("art", "image://albumart/artist=Spiderbait&album=Spiderbait"); } function test_query() { var songs = store.query("unknown", MediaStore.AudioMedia); compare(songs.length, 0, "songs.length == 0"); var songs = store.query("Pony", MediaStore.AudioMedia); compare(songs.length, 1, "songs.length == 1"); compare(songs[0].title, "Buy Me a Pony"); } } } mediascanner2-0.100+14.04.20140403/test/qml/tst_songsearchmodel.qml0000644000015301777760000000061612317230414025152 0ustar pbusernogroup00000000000000import QtQuick 2.0 import QtTest 1.0 import Ubuntu.MediaScanner 0.1 Item { id: root MediaStore { id: store } SongsSearchModel { id: songs_model store: store query: "" } ListView { id: songs_view model: songs_model } TestCase { name: "SongsSearchModelTests" function test_foo() { } } } mediascanner2-0.100+14.04.20140403/test/basic.cc0000644000015301777760000001512012317230414021163 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include #include #include #include #include #include #include #include "test_config.h" #include #include #include #include #include #include #include using namespace std; using namespace mediascanner; class ScanTest : public ::testing::Test { protected: ScanTest() { } virtual ~ScanTest() { } virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(ScanTest, init) { MediaStore store(":memory:", MS_READ_WRITE); MetadataExtractor extractor; InvalidationSender invalidator; SubtreeWatcher watcher(store, extractor, invalidator); } void clear_dir(const string &subdir) { string cmd = "rm -rf " + subdir; ASSERT_EQ(system(cmd.c_str()), 0); // Because I like to live dangerously, that's why. } void copy_file(const string &src, const string &dst) { FILE* f = fopen(src.c_str(), "r"); ASSERT_TRUE(f); fseek(f, 0, SEEK_END); size_t size = ftell(f); char* buf = new char[size]; fseek(f, 0, SEEK_SET); ASSERT_EQ(fread(buf, 1, size, f), size); fclose(f); f = fopen(dst.c_str(), "w"); ASSERT_TRUE(f); ASSERT_EQ(fwrite(buf, 1, size, f), size); fclose(f); delete[] buf; } void iterate_main_loop() { while (g_main_context_iteration(nullptr, FALSE)) { } } TEST_F(ScanTest, index) { string subdir = TEST_DIR "/testdir"; string testfile = SOURCE_DIR "/media/testfile.ogg"; string outfile = subdir + "/testfile.ogg"; clear_dir(subdir); ASSERT_GE(mkdir(subdir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR), 0); MediaStore store(":memory:", MS_READ_WRITE); MetadataExtractor extractor; InvalidationSender invalidator; invalidator.disable(); SubtreeWatcher watcher(store, extractor, invalidator); watcher.addDir(subdir); ASSERT_EQ(store.size(), 0); copy_file(testfile, outfile); iterate_main_loop(); ASSERT_EQ(store.size(), 1); ASSERT_EQ(unlink(outfile.c_str()), 0); iterate_main_loop(); ASSERT_EQ(store.size(), 0); } TEST_F(ScanTest, subdir) { string testdir = TEST_DIR "/testdir"; string subdir = testdir + "/subdir"; string testfile = SOURCE_DIR "/media/testfile.ogg"; string outfile = subdir + "/testfile.ogg"; clear_dir(testdir); ASSERT_GE(mkdir(testdir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR), 0); MediaStore store(":memory:", MS_READ_WRITE); MetadataExtractor extractor; InvalidationSender invalidator; invalidator.disable(); SubtreeWatcher watcher(store, extractor, invalidator); ASSERT_EQ(watcher.directoryCount(), 0); watcher.addDir(testdir); ASSERT_EQ(watcher.directoryCount(), 1); ASSERT_EQ(store.size(), 0); ASSERT_GE(mkdir(subdir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR), 0); iterate_main_loop(); ASSERT_EQ(watcher.directoryCount(), 2); copy_file(testfile, outfile); iterate_main_loop(); ASSERT_EQ(store.size(), 1); ASSERT_EQ(unlink(outfile.c_str()), 0); iterate_main_loop(); ASSERT_EQ(store.size(), 0); ASSERT_EQ(rmdir(subdir.c_str()), 0); iterate_main_loop(); ASSERT_EQ(watcher.directoryCount(), 1); } // FIXME move this somewhere in the implementation. void scanFiles(MediaStore &store, const string &subdir, const MediaType type) { Scanner s; MetadataExtractor extractor; vector files = s.scanFiles(&extractor, subdir, type); for(auto &d : files) { // If the file is unchanged, skip it. if (d.etag == store.getETag(d.filename)) continue; store.insert(extractor.extract(d)); } } TEST_F(ScanTest, scan) { string dbname("scan-mediastore.db"); string testdir = TEST_DIR "/testdir"; string testfile = SOURCE_DIR "/media/testfile.ogg"; string outfile = testdir + "/testfile.ogg"; unlink(dbname.c_str()); clear_dir(testdir); ASSERT_GE(mkdir(testdir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR), 0); copy_file(testfile, outfile); MediaStore *store = new MediaStore(dbname, MS_READ_WRITE); scanFiles(*store, testdir, AudioMedia); ASSERT_EQ(store->size(), 1); delete store; unlink(outfile.c_str()); store = new MediaStore(dbname, MS_READ_WRITE); store->pruneDeleted(); ASSERT_EQ(store->size(), 0); delete store; } TEST_F(ScanTest, scan_skips_unchanged_files) { string testdir = TEST_DIR "/testdir"; string testfile = SOURCE_DIR "/media/testfile.ogg"; string outfile = testdir + "/testfile.ogg"; clear_dir(testdir); ASSERT_GE(mkdir(testdir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR), 0); copy_file(testfile, outfile); MediaStore store(":memory:", MS_READ_WRITE); scanFiles(store, testdir, AudioMedia); ASSERT_EQ(store.size(), 1); /* Modify the metadata for this file in the database */ MediaFile media = store.lookup(outfile); EXPECT_NE(media.getETag(), ""); EXPECT_EQ(media.getTitle(), "track1"); MediaFileBuilder mfb(media); mfb.setTitle("something else"); store.insert(mfb.build()); /* Scan again, and note that the data hasn't been updated */ scanFiles(store, testdir, AudioMedia); media = store.lookup(outfile); EXPECT_EQ(media.getTitle(), "something else"); /* Now change the stored etag, to trigger an update */ MediaFileBuilder mfb2(media); mfb2.setETag("old-etag"); store.insert(mfb2.build()); scanFiles(store, testdir, AudioMedia); media = store.lookup(outfile); EXPECT_EQ(media.getTitle(), "track1"); } TEST(Mediascanner, root_skip) { MetadataExtractor e; string root(SOURCE_DIR); Scanner s; auto res = s.scanFiles(&e, root, AudioMedia); ASSERT_EQ(res.size(), 1); } int main(int argc, char **argv) { gst_init (&argc, &argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } mediascanner2-0.100+14.04.20140403/COPYING.GPL0000644000015301777760000010451312317230414020275 0ustar pbusernogroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mediascanner2-0.100+14.04.20140403/mediascanner-2.0.pc.in0000644000015301777760000000040512317230414022473 0ustar pbusernogroup00000000000000Name: mediascanner-2.0 Description: Access library for the media scanner's index Version: @MEDIASCANNER_VERSION@ Requires.private: gio-2.0 sqlite3 Libs: -L@CMAKE_INSTALL_FULL_LIBDIR@ -lmediascanner-2.0 Cflags: -I@CMAKE_INSTALL_FULL_INCLUDEDIR@/mediascanner-2.0 mediascanner2-0.100+14.04.20140403/mediascanner-2.0.conf0000644000015301777760000000022512317230414022411 0ustar pbusernogroup00000000000000description "Media Scanner" author "James Henstridge " start on started dbus respawn exec mediascanner-service-2.0 mediascanner2-0.100+14.04.20140403/CMakeLists.txt0000644000015301777760000000267512317230431021366 0ustar pbusernogroup00000000000000project(scantest CXX C) cmake_minimum_required(VERSION 2.8.9) set(MEDIASCANNER_VERSION "0.99") if(PROJECT_BINARY_DIR STREQUAL PROJECT_SOURCE_DIR) message(FATAL_ERROR "In-tree build attempt detected, aborting. Set your build dir outside your source dir, delete CMakeCache.txt from source root and try again.") endif() option(full_warnings "All possible compiler warnings." OFF) include(FindPkgConfig) pkg_check_modules(MEDIASCANNER REQUIRED gio-2.0 sqlite3 ) pkg_check_modules(GST gstreamer-1.0 gstreamer-pbutils-1.0 REQUIRED) find_package(Threads REQUIRED) find_package(Qt5Core REQUIRED) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra -std=c99") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra -std=c++11") if(${full_warnings}) # C does not have any more warning flags. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weffc++") endif() include(cmake/coverage.cmake) include(GNUInstallDirs) set(LIBDIR $CMAKE_INSTALL_LIBDIR) enable_testing() add_subdirectory(src/mediascanner) add_subdirectory(src/daemon) add_subdirectory(src/qml/Ubuntu/MediaScanner) add_subdirectory(src/utils) add_subdirectory(test) # Install pkg-config file configure_file(mediascanner-2.0.pc.in mediascanner-2.0.pc) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mediascanner-2.0.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # Install Upstart user session job install( FILES mediascanner-2.0.conf DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions ) mediascanner2-0.100+14.04.20140403/src/0000755000015301777760000000000012317231021017377 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/daemon/0000755000015301777760000000000012317231021020642 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/daemon/Scanner.cc0000644000015301777760000000467512317230423022563 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include "MetadataExtractor.hh" #include "Scanner.hh" #include "util.h" #include #include #include #include using namespace std; namespace mediascanner { Scanner::Scanner() { } Scanner::~Scanner() { } vector Scanner::scanFiles(MetadataExtractor *extractor, const std::string &root, const MediaType type) { vector result; unique_ptr dir(opendir(root.c_str()), closedir); printf("In subdir %s\n", root.c_str()); if(!dir) { return result; } if(is_rootlike(root)) { fprintf(stderr, "Directory %s looks like a top level root directory, skipping it (%s).\n", root.c_str(), __PRETTY_FUNCTION__); return result; } unique_ptr entry((dirent*)malloc(sizeof(dirent) + NAME_MAX), free); struct dirent *de; while(readdir_r(dir.get(), entry.get(), &de) == 0 && de ) { struct stat statbuf; string fname = entry.get()->d_name; if(fname[0] == '.') // Ignore hidden files and dirs. continue; string fullpath = root + "/" + fname; lstat(fullpath.c_str(), &statbuf); if(S_ISREG(statbuf.st_mode)) { try { DetectedFile d = extractor->detect(fullpath); if (type == AllMedia || d.type == type) { result.push_back(d); } } catch (const exception &e) { /* Ignore non-media files */ } } else if(S_ISDIR(statbuf.st_mode)) { vector subdir = scanFiles(extractor, fullpath, type); result.insert(result.end(), subdir.begin(), subdir.end()); } } return result; } } mediascanner2-0.100+14.04.20140403/src/daemon/MetadataExtractor.hh0000644000015301777760000000321012317230414024600 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #ifndef METADATAEXTRACTOR_H #define METADATAEXTRACTOR_H #include #include "../mediascanner/scannercore.hh" namespace mediascanner { class MediaFile; struct MetadataExtractorPrivate; struct DetectedFile { DetectedFile(const std::string &filename, const std::string &etag, const std::string content_type, MediaType type) : filename(filename), etag(etag), content_type(content_type) , type(type) {} std::string filename; std::string etag; std::string content_type; MediaType type; }; class MetadataExtractor final { public: MetadataExtractor(int seconds=25); ~MetadataExtractor(); MetadataExtractor(const MetadataExtractor&) = delete; MetadataExtractor& operator=(MetadataExtractor &o) = delete; DetectedFile detect(const std::string &filename); MediaFile extract(const DetectedFile &media); private: MetadataExtractorPrivate *p; }; } #endif mediascanner2-0.100+14.04.20140403/src/daemon/InvalidationSender.cc0000644000015301777760000000376412317230414024752 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include"InvalidationSender.hh" #include #include #include #include using namespace std; // timer delay in seconds const unsigned int DELAY = 1; InvalidationSender::InvalidationSender() : enabled(true), timeout_id(0) { } InvalidationSender::~InvalidationSender() { if (timeout_id != 0) { g_source_remove(timeout_id); } } void InvalidationSender::invalidate() { if (!enabled) { return; } if (timeout_id != 0) { return; } timeout_id = g_timeout_add_seconds(DELAY, &InvalidationSender::callback, static_cast(this)); } int InvalidationSender::callback(void *data) { auto invalidator = static_cast(data); string invocation("dbus-send /com/canonical/unity/scopes "); invocation += "com.canonical.unity.scopes.InvalidateResults string:"; const string m_invoc = invocation + "mediascanner-music"; const string v_invoc = invocation + "mediascanner-video"; if(system(m_invoc.c_str()) != 0) { fprintf(stderr, "Could not invalidate music scope results.\n"); } if(system(v_invoc.c_str()) != 0) { fprintf(stderr, "Could not invalidate video scope results.\n"); } invalidator->timeout_id = 0; return FALSE; } void InvalidationSender::disable() { enabled = false; } mediascanner2-0.100+14.04.20140403/src/daemon/Scanner.hh0000644000015301777760000000207212317230414022562 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #ifndef SCANNER_HH_ #define SCANNER_HH_ #include #include #include namespace mediascanner { class DetectedFile; class MetadataExtractor; class Scanner final { public: Scanner(); ~Scanner(); std::vector scanFiles(MetadataExtractor *extractor, const std::string &root, const MediaType type); }; } #endif mediascanner2-0.100+14.04.20140403/src/daemon/util.h0000644000015301777760000000146512317230414022003 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #ifndef MEDIASCANNER_UTIL #define MEDIASCANNER_UTIL #include bool is_rootlike(const std::string &path); #endif mediascanner2-0.100+14.04.20140403/src/daemon/MetadataExtractor.cc0000644000015301777760000001370012317230465024601 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include "../mediascanner/MediaFile.hh" #include "../mediascanner/MediaFileBuilder.hh" #include "../mediascanner/internal/utils.hh" #include "MetadataExtractor.hh" #include #include #include #include #include #include #include #include using namespace std; namespace mediascanner { struct MetadataExtractorPrivate { std::unique_ptr discoverer; MetadataExtractorPrivate() : discoverer(nullptr, g_object_unref) {}; }; MetadataExtractor::MetadataExtractor(int seconds) { p = new MetadataExtractorPrivate(); GError *error = nullptr; p->discoverer.reset(gst_discoverer_new(GST_SECOND * seconds, &error)); if (not p->discoverer) { string errortxt(error->message); g_error_free(error); delete(p); string msg = "Failed to create discoverer: "; msg += errortxt; throw runtime_error(msg); } if (error) { // Sometimes this is filled in even though no error happens. g_error_free(error); } } MetadataExtractor::~MetadataExtractor() { delete p; } DetectedFile MetadataExtractor::detect(const std::string &filename) { std::unique_ptr file( g_file_new_for_path(filename.c_str()), g_object_unref); if (!file) { throw runtime_error("Could not create file object"); } GError *error = nullptr; std::unique_ptr info( g_file_query_info( file.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE "," G_FILE_ATTRIBUTE_ETAG_VALUE, G_FILE_QUERY_INFO_NONE, /* cancellable */ NULL, &error), g_object_unref); if (!info) { string errortxt(error->message); g_error_free(error); string msg("Query of file info for "); msg += filename; msg += " failed: "; msg += errortxt; throw runtime_error(msg); } string etag(g_file_info_get_etag(info.get())); string content_type(g_file_info_get_attribute_string( info.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE)); if (content_type.empty()) { throw runtime_error("Could not determine content type."); } MediaType type; if (content_type.find("audio/") == 0) { type = AudioMedia; } else if (content_type.find("video/") == 0) { type = VideoMedia; } else { throw runtime_error(string("File ") + filename + " is not audio or video"); } return DetectedFile(filename, etag, content_type, type); } static void extract_tag_info (const GstTagList * list, const gchar * tag, gpointer user_data) { MediaFileBuilder *mfb = (MediaFileBuilder *) user_data; int i, num; string tagname(tag); num = gst_tag_list_get_tag_size (list, tag); for (i = 0; i < num; ++i) { const GValue *val; val = gst_tag_list_get_value_index (list, tag, i); if (G_VALUE_HOLDS_STRING(val)) { if (tagname == GST_TAG_ARTIST) mfb->setAuthor(g_value_get_string(val)); else if (tagname == GST_TAG_TITLE) mfb->setTitle(g_value_get_string(val)); else if (tagname == GST_TAG_ALBUM) mfb->setAlbum(g_value_get_string(val)); else if (tagname == GST_TAG_ALBUM_ARTIST) mfb->setAlbumArtist(g_value_get_string(val)); } else if (G_VALUE_HOLDS(val, GST_TYPE_DATE_TIME)) { if (tagname == GST_TAG_DATE_TIME) { GstDateTime *dt = static_cast(g_value_get_boxed(val)); char *dt_string = gst_date_time_to_iso8601_string(dt); mfb->setDate(dt_string); g_free(dt_string); } } else if (G_VALUE_HOLDS_UINT(val)) { if (tagname == GST_TAG_TRACK_NUMBER) { mfb->setTrackNumber(g_value_get_uint(val)); } } } } MediaFile MetadataExtractor::extract(const DetectedFile &d) { printf("Extracting metadata from %s.\n", d.filename.c_str()); MediaFileBuilder mfb(d.filename); mfb.setETag(d.etag); mfb.setContentType(d.content_type); mfb.setType(d.type); string uri = getUri(d.filename); GError *error = nullptr; unique_ptr info( gst_discoverer_discover_uri(p->discoverer.get(), uri.c_str(), &error), g_object_unref); if (info.get() == NULL) { string errortxt(error->message); g_error_free(error); string msg = "Discovery of file "; msg += d.filename; msg += " failed: "; msg += errortxt; throw runtime_error(msg); } if (error) { // Sometimes this gets filled in even if no error actually occurs. g_error_free(error); error = nullptr; } if (gst_discoverer_info_get_result(info.get()) != GST_DISCOVERER_OK) { throw runtime_error("Unable to discover file " + d.filename); } const GstTagList *tags = gst_discoverer_info_get_tags(info.get()); if (tags != NULL) { gst_tag_list_foreach(tags, extract_tag_info, &mfb); } mfb.setDuration(static_cast( gst_discoverer_info_get_duration(info.get())/GST_SECOND)); return mfb.build(); } } mediascanner2-0.100+14.04.20140403/src/daemon/InvalidationSender.hh0000644000015301777760000000232212317230414024751 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #ifndef INVALIDATIONSENDER_HH #define INVALIDATIONSENDER_HH /** * A class that sends a broadcast signal that the state of media * files has changed. */ class InvalidationSender final { public: InvalidationSender(); ~InvalidationSender(); InvalidationSender(const InvalidationSender &o) = delete; InvalidationSender& operator=(const InvalidationSender &o) = delete; void invalidate(); void disable(); private: static int callback(void *data); bool enabled; unsigned int timeout_id; }; #endif mediascanner2-0.100+14.04.20140403/src/daemon/SubtreeWatcher.cc0000644000015301777760000002041212317230423024104 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include "../mediascanner/MediaStore.hh" #include "../mediascanner/MediaFile.hh" #include "InvalidationSender.hh" #include "MetadataExtractor.hh" #include "SubtreeWatcher.hh" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; namespace mediascanner { struct SubtreeWatcherPrivate { MediaStore &store; // Hackhackhack, should be replaced with callback object or something. MetadataExtractor &extractor; InvalidationSender &invalidator; int inotifyid; // Ideally use boost::bimap or something instead of these two separate objects. std::map wd2str; std::map str2wd; bool keep_going; std::unique_ptr source; SubtreeWatcherPrivate(MediaStore &store, MetadataExtractor &extractor, InvalidationSender &invalidator) : store(store), extractor(extractor), invalidator(invalidator), inotifyid(inotify_init()), keep_going(true), source(g_unix_fd_source_new(inotifyid, G_IO_IN), g_source_unref) { } ~SubtreeWatcherPrivate() { for(auto &i : wd2str) { inotify_rm_watch(inotifyid, i.first); } close(inotifyid); } }; static gboolean source_callback(GIOChannel *, GIOCondition, gpointer data) { SubtreeWatcher *watcher = static_cast(data); watcher->processEvents(); return TRUE; } SubtreeWatcher::SubtreeWatcher(MediaStore &store, MetadataExtractor &extractor, InvalidationSender &invalidator) { p = new SubtreeWatcherPrivate(store, extractor, invalidator); if(p->inotifyid == -1) { string msg("Could not init inotify: "); msg += strerror(errno); delete p; throw runtime_error(msg); } g_source_set_callback(p->source.get(), reinterpret_cast(source_callback), static_cast(this), nullptr); g_source_attach(p->source.get(), nullptr); } SubtreeWatcher::~SubtreeWatcher() { g_source_destroy(p->source.get()); delete p; } void SubtreeWatcher::addDir(const string &root) { if(root[0] != '/') throw runtime_error("Path must be absolute."); if(is_rootlike(root)) { fprintf(stderr, "Directory %s looks like a top level root directory, skipping it (%s).\n", root.c_str(), __PRETTY_FUNCTION__); return; } if(p->str2wd.find(root) != p->str2wd.end()) return; unique_ptr dir(opendir(root.c_str()), closedir); if(!dir) { return; } int wd = inotify_add_watch(p->inotifyid, root.c_str(), IN_CREATE | IN_DELETE_SELF | IN_DELETE | IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_ONLYDIR); if(wd == -1) { fprintf(stderr, "Could not create inotify watch object: %s\n", strerror(errno)); return; // Probably ran out of watches, keep monitoring what we can. } p->wd2str[wd] = root; p->str2wd[root] = wd; printf("Watching subdirectory %s, %ld watches in total.\n", root.c_str(), (long)p->wd2str.size()); unique_ptr entry((dirent*)malloc(sizeof(dirent) + NAME_MAX), free); struct dirent *de; while(readdir_r(dir.get(), entry.get(), &de) == 0 && de ) { struct stat statbuf; string fname = entry.get()->d_name; if(fname[0] == '.') // Ignore hidden entries and also "." and "..". continue; string fullpath = root + "/" + fname; lstat(fullpath.c_str(), &statbuf); if(S_ISDIR(statbuf.st_mode)) { addDir(fullpath); } } } bool SubtreeWatcher::removeDir(const string &abspath) { if(p->str2wd.find(abspath) == p->str2wd.end()) return false; int wd = p->str2wd[abspath]; inotify_rm_watch(p->inotifyid, wd); p->wd2str.erase(wd); p->str2wd.erase(abspath); printf("Stopped watching %s, %ld directories remain.\n", abspath.c_str(), (long)p->wd2str.size()); if(p->wd2str.empty()) p->keep_going = false; return true; } void SubtreeWatcher::fileAdded(const string &abspath) { printf("New file was created: %s.\n", abspath.c_str()); try { DetectedFile d = p->extractor.detect(abspath); // Only extract and insert the file if the ETag has changed. if (d.etag != p->store.getETag(d.filename)) { p->store.insert(p->extractor.extract(d)); } } catch(const exception &e) { fprintf(stderr, "Error when adding new file: %s\n", e.what()); } } void SubtreeWatcher::fileDeleted(const string &abspath) { printf("File was deleted: %s\n", abspath.c_str()); p->store.remove(abspath); } void SubtreeWatcher::dirAdded(const string &abspath) { printf("New directory was created: %s.\n", abspath.c_str()); addDir(abspath); } void SubtreeWatcher::dirRemoved(const string &abspath) { printf("Subdirectory was deleted: %s.\n", abspath.c_str()); removeDir(abspath); } void SubtreeWatcher::processEvents() { const int BUFSIZE=4096; char buf[BUFSIZE]; bool changed = false; ssize_t num_read; num_read = read(p->inotifyid, buf, BUFSIZE); if(num_read == 0) { printf("Inotify returned 0.\n"); return; } if(num_read == -1) { printf("Read error.\n"); return; } for(char *d = buf; d < buf + num_read;) { struct inotify_event *event = (struct inotify_event *) d; if (p->wd2str.find(event->wd) == p->wd2str.end()) { // Ignore events for unknown watches. We may receive // such events when a directory is removed. d += sizeof(struct inotify_event) + event->len; continue; } string directory = p->wd2str[event->wd]; string filename(event->name); string abspath = directory + '/' + filename; bool is_dir = false; bool is_file = false; struct stat statbuf; lstat(abspath.c_str(), &statbuf); // Remember: these are not valid in case of delete event. if(S_ISDIR(statbuf.st_mode)) is_dir = true; if(S_ISREG(statbuf.st_mode)) is_file = true; if(event->mask & IN_CREATE) { if(is_dir) { dirAdded(abspath); changed = true; } // Do not add files upon creation because we can't parse // their metadata until it is fully written. } else if((event->mask & IN_CLOSE_WRITE) || (event->mask & IN_MOVED_TO)) { if(is_dir) { dirAdded(abspath); changed = true; } if(is_file) { fileAdded(abspath); changed = true; } } else if((event->mask & IN_DELETE) || (event->mask & IN_MOVED_FROM)) { if(p->str2wd.find(abspath) != p->str2wd.end()) { dirRemoved(abspath); changed = true; } else { fileDeleted(abspath); changed = true; } } else if((event->mask & IN_IGNORED) || (event->mask & IN_UNMOUNT) || (event->mask & IN_DELETE_SELF)) { removeDir(abspath); changed = true; } d += sizeof(struct inotify_event) + event->len; } if (changed) { p->invalidator.invalidate(); } } int SubtreeWatcher::getFd() const { return p->inotifyid; } int SubtreeWatcher::directoryCount() const { return (int) p->wd2str.size(); } } mediascanner2-0.100+14.04.20140403/src/daemon/scannerdaemon.cc0000644000015301777760000002105412317230423023775 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mediascanner/MediaFile.hh" #include "../mediascanner/MediaStore.hh" #include "MetadataExtractor.hh" #include "SubtreeWatcher.hh" #include "Scanner.hh" #include "InvalidationSender.hh" #include "util.h" using namespace std; using namespace mediascanner; class ScannerDaemon final { public: ScannerDaemon(); ~ScannerDaemon(); int run(); private: void setupSignals(); void setupMountWatcher(); void readFiles(MediaStore &store, const string &subdir, const MediaType type); void addDir(const string &dir); void removeDir(const string &dir); static gboolean sourceCallback(int, GIOCondition, gpointer data); static gboolean signalCallback(gpointer data); void processEvents(); void addMountedVolumes(); int mountfd; unique_ptr mount_source; int sigint_id, sigterm_id; string mountDir; string cachedir; unique_ptr store; unique_ptr extractor; map> subtrees; InvalidationSender invalidator; unique_ptr main_loop; }; static std::string getCurrentUser() { int uid = geteuid(); struct passwd *pwd = getpwuid(uid); if (pwd == NULL) { string msg("Could not look up user name: "); msg += strerror(errno); throw runtime_error(msg); } return pwd->pw_name; } ScannerDaemon::ScannerDaemon() : mount_source(nullptr, g_source_unref), sigint_id(0), sigterm_id(0), main_loop(g_main_loop_new(nullptr, FALSE), g_main_loop_unref) { mountDir = string("/media/") + getCurrentUser(); unique_ptr tmp(new MediaStore(MS_READ_WRITE, "/media/")); store = move(tmp); extractor.reset(new MetadataExtractor()); setupMountWatcher(); addMountedVolumes(); const char *musicdir = g_get_user_special_dir(G_USER_DIRECTORY_MUSIC); if (musicdir) addDir(musicdir); const char *videodir = g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS); if (videodir) addDir(videodir); // In case someone opened the db file before we could populate it. invalidator.invalidate(); // This is at the end because the initial scan may take a while // and is not interruptible but we want the process to die if it // gets a SIGINT or the like. setupSignals(); } ScannerDaemon::~ScannerDaemon() { if (sigint_id != 0) { g_source_remove(sigint_id); } if (sigterm_id != 0) { g_source_remove(sigterm_id); } if (mount_source) { g_source_destroy(mount_source.get()); } close(mountfd); } gboolean ScannerDaemon::signalCallback(gpointer data) { ScannerDaemon *daemon = static_cast(data); g_main_loop_quit(daemon->main_loop.get()); return TRUE; } void ScannerDaemon::setupSignals() { sigint_id = g_unix_signal_add(SIGINT, &ScannerDaemon::signalCallback, this); sigterm_id = g_unix_signal_add(SIGTERM, &ScannerDaemon::signalCallback, this); } void ScannerDaemon::addDir(const string &dir) { assert(dir[0] == '/'); if(subtrees.find(dir) != subtrees.end()) { return; } if(is_rootlike(dir)) { fprintf(stderr, "Directory %s looks like a top level root directory, skipping it (%s).\n", dir.c_str(), __PRETTY_FUNCTION__); return; } unique_ptr sw(new SubtreeWatcher(*store.get(), *extractor.get(), invalidator)); store->restoreItems(dir); store->pruneDeleted(); readFiles(*store.get(), dir, AllMedia); sw->addDir(dir); subtrees[dir] = move(sw); } void ScannerDaemon::removeDir(const string &dir) { assert(dir[0] == '/'); assert(subtrees.find(dir) != subtrees.end()); subtrees.erase(dir); } void ScannerDaemon::readFiles(MediaStore &store, const string &subdir, const MediaType type) { Scanner s; vector files = s.scanFiles(extractor.get(), subdir, type); for(auto &d : files) { // If the file is unchanged, skip it. if (d.etag == store.getETag(d.filename)) continue; try { store.insert(extractor->extract(d)); } catch(const exception &e) { fprintf(stderr, "Error when indexing: %s\n", e.what()); } } } int ScannerDaemon::run() { g_main_loop_run(main_loop.get()); return 99; } gboolean ScannerDaemon::sourceCallback(int, GIOCondition, gpointer data) { ScannerDaemon *daemon = static_cast(data); daemon->processEvents(); return TRUE; } void ScannerDaemon::setupMountWatcher() { mountfd = inotify_init(); if(mountfd < 0) { string msg("Could not init inotify: "); msg += strerror(errno); throw runtime_error(msg); } int wd = inotify_add_watch(mountfd, mountDir.c_str(), IN_CREATE | IN_DELETE | IN_ONLYDIR); if(wd == -1) { if (errno == ENOENT) { printf("Mount directory does not exist\n"); return; } string msg("Could not create inotify watch object: "); msg += strerror(errno); throw runtime_error(msg); } mount_source.reset(g_unix_fd_source_new(mountfd, G_IO_IN)); g_source_set_callback(mount_source.get(), reinterpret_cast(&ScannerDaemon::sourceCallback), this, nullptr); g_source_attach(mount_source.get(), nullptr); } void ScannerDaemon::processEvents() { const int BUFSIZE= 4096; char buf[BUFSIZE]; bool changed = false; ssize_t num_read; num_read = read(mountfd, buf, BUFSIZE); if(num_read == 0) { printf("Inotify returned 0.\n"); return; } if(num_read == -1) { printf("Read error.\n"); return; } for(char *p = buf; p < buf + num_read;) { struct inotify_event *event = (struct inotify_event *) p; string directory = mountDir; string filename(event->name); string abspath = directory + '/' + filename; struct stat statbuf; lstat(abspath.c_str(), &statbuf); if(S_ISDIR(statbuf.st_mode)) { if(event->mask & IN_CREATE) { printf("Volume %s was mounted.\n", abspath.c_str()); addDir(abspath); changed = true; } else if(event->mask & IN_DELETE){ printf("Volume %s was unmounted.\n", abspath.c_str()); if(subtrees.find(abspath) != subtrees.end()) { removeDir(abspath); changed = true; } else { // This volume was not tracked because it looked rootlike. // Thus we don't need to do anything. } } } p += sizeof(struct inotify_event) + event->len; } if (changed) { invalidator.invalidate(); } } void ScannerDaemon::addMountedVolumes() { unique_ptr dir(opendir(mountDir.c_str()), closedir); if(!dir) { return; } unique_ptr entry((dirent*)malloc(sizeof(dirent) + NAME_MAX), free); struct dirent *de; while(readdir_r(dir.get(), entry.get(), &de) == 0 && de ) { struct stat statbuf; string fname = entry.get()->d_name; if(fname[0] == '.') continue; string fullpath = mountDir + "/" + fname; lstat(fullpath.c_str(), &statbuf); if(S_ISDIR(statbuf.st_mode)) { addDir(fullpath); } } } int main(int argc, char **argv) { gst_init (&argc, &argv); try { ScannerDaemon d; return d.run(); } catch(string &s) { printf("Error: %s\n", s.c_str()); } return 100; } mediascanner2-0.100+14.04.20140403/src/daemon/CMakeLists.txt0000644000015301777760000000104512317230414023407 0ustar pbusernogroup00000000000000add_definitions(${MEDIASCANNER_CFLAGS} ${GST_CFLAGS}) include_directories(..) add_library(scannerstuff STATIC InvalidationSender.cc MetadataExtractor.cc SubtreeWatcher.cc Scanner.cc util.cc ) target_link_libraries(scannerstuff ${GST_LIBRARIES}) add_executable(scannerdaemon scannerdaemon.cc ) set_target_properties(scannerdaemon PROPERTIES OUTPUT_NAME "mediascanner-service-2.0") target_link_libraries(scannerdaemon mediascanner scannerstuff ) install( TARGETS scannerdaemon RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) mediascanner2-0.100+14.04.20140403/src/daemon/SubtreeWatcher.hh0000644000015301777760000000306712317230414024125 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #ifndef SUBTREEWATCHER_HH_ #define SUBTREEWATCHER_HH_ #include class InvalidationSender; namespace mediascanner { class MediaStore; class MetadataExtractor; struct SubtreeWatcherPrivate; class SubtreeWatcher final { private: SubtreeWatcherPrivate *p; void fileAdded(const std::string &abspath); void fileDeleted(const std::string &abspath); void dirAdded(const std::string &abspath); void dirRemoved(const std::string &abspath); bool removeDir(const std::string &abspath); public: SubtreeWatcher(MediaStore &store, MetadataExtractor &extractor, InvalidationSender &invalidator); ~SubtreeWatcher(); SubtreeWatcher(SubtreeWatcher &o) = delete; SubtreeWatcher& operator=(SubtreeWatcher &o) = delete; void addDir(const std::string &path); void processEvents(); int getFd() const; int directoryCount() const; }; } #endif mediascanner2-0.100+14.04.20140403/src/daemon/util.cc0000644000015301777760000000253012317230440022132 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include "util.h" #include #include using namespace std; static bool dir_exists(const string &path) { struct stat statbuf; if(stat(path.c_str(), &statbuf) < 0) { if(errno != ENOENT) { printf("Error while trying to determine state of dir %s: %s\n", path.c_str(), strerror(errno)); } return false; } return S_ISDIR(statbuf.st_mode) ; } bool is_rootlike(const string &path) { string s1 = path + "/usr"; string s2 = path + "/var"; string s3 = path + "/bin"; string s4 = path + "/Program Files"; return (dir_exists(s1) && dir_exists(s2) && dir_exists(s3)) || dir_exists(s4); } mediascanner2-0.100+14.04.20140403/src/utils/0000755000015301777760000000000012317231021020537 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/utils/query.cc0000644000015301777760000000327312317230414022225 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include "mediascanner/MediaFile.hh" #include "mediascanner/MediaStore.hh" #include #include #include #include using namespace std; using namespace mediascanner; void queryDb(const string &core_term) { MediaStore store(MS_READ_ONLY); vector results; results = store.query(core_term, AudioMedia); if(results.empty()) { printf("No audio matches.\n"); } else { printf("Audio matches:\n"); } for(const auto &i : results) { printf("Filename: %s\n", i.getFileName().c_str()); } results = store.query(core_term, VideoMedia); if(results.empty()) { printf("No video matches.\n"); } else { printf("Video matches:\n"); } for(const auto &i : results) { printf("Filename: %s\n", i.getFileName().c_str()); } } int main(int argc, char **argv) { if(argc < 2) { printf("%s \n", argv[0]); return 1; } string core_term(argv[1]); queryDb(core_term); } mediascanner2-0.100+14.04.20140403/src/utils/CMakeLists.txt0000644000015301777760000000047012317230431023304 0ustar pbusernogroup00000000000000add_definitions(${MEDIASCANNER_CFLAGS}) include_directories(..) add_executable(watcher watchertest.cc) add_executable(query query.cc) target_link_libraries(query mediascanner ${MEDIASCANNER_LDFLAGS}) add_executable(scaletest scaletest.cc) target_link_libraries(scaletest mediascanner ${MEDIASCANNER_LDFLAGS}) mediascanner2-0.100+14.04.20140403/src/utils/scaletest.cc0000644000015301777760000000662512317230414023053 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include "mediascanner/MediaStore.hh" #include "mediascanner/MediaFile.hh" #include "mediascanner/internal/utils.hh" #include #include #include #include #include #include using namespace std; using namespace mediascanner; #define RNDWORD words[rnd() % words.size()] int printer(void*/*arg*/, int num_cols, char **data, char **colnames) { for(int i=0; i words; random_device rnd; while(getline(ifile, line)) { if(line.length() >=3) { if(line[line.length()-2] == '\'') continue; } words.push_back(line); } int i=0; for(int artistCount=0; artistCount < 1000; artistCount++) { string artist = RNDWORD + " " + RNDWORD; for(int albumCount=0; albumCount < 3; albumCount++) { string album = RNDWORD + " " + RNDWORD + " " + RNDWORD; for(int trackCount=0; trackCount < 10; trackCount++) { int numWords = rnd() % 4 + 1; string track(RNDWORD); for(int i=1; i * * This library is free software; you can redistribute it and/or modify it under * the terms of version 3 of the GNU General Public License as published * by the Free Software Foundation. * * This library 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 . */ #include #include #include #include using namespace std; #define BUFSIZE 4096 int main(int /*argc*/, char **/*argv*/) { string dbdir = "/home/"; dbdir += getlogin(); dbdir += "/.cache/mediascanner-test"; int ifd; int wd; char buf[BUFSIZE]; ifd = inotify_init(); if(ifd == -1) { printf("Inotify init failed.\n"); return 1; } wd = inotify_add_watch(ifd, dbdir.c_str(), IN_CREATE | IN_DELETE | IN_MODIFY); if(wd == -1) { printf("Could not create watch for data dir.\n"); return 1; } printf("This program simulates invalidation of query results as media files come and go.\n\n"); while(true) { ssize_t num_read; num_read = read(ifd, buf, BUFSIZE); if(num_read == 0) { printf("Inotify returned 0.\n"); return 1; } if(num_read == -1) { printf("Read error.\n"); return 1; } for(char *p = buf; p < buf + num_read;) { struct inotify_event *event = (struct inotify_event *) p; if(event->mask & IN_CREATE) { printf("Invalidation: create\n"); } else if(event->mask & IN_DELETE) { printf("Invalidation: delete\n"); } else { printf("Invalidation: modify %s\n", event->name); } p += sizeof(struct inotify_event) + event->len; } } return 0; } mediascanner2-0.100+14.04.20140403/src/mediascanner/0000755000015301777760000000000012317231021022030 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/mediascanner/internal/0000755000015301777760000000000012317231021023644 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/mediascanner/internal/utils.hh0000644000015301777760000000171312317230414025334 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SCAN_UTILS_H #define SCAN_UTILS_H #include namespace mediascanner { std::string sqlQuote(const std::string &input); std::string filenameToTitle(const std::string &filename); std::string getUri(const std::string &filename); } #endif mediascanner2-0.100+14.04.20140403/src/mediascanner/internal/sqliteutils.hh0000644000015301777760000000620412317230414026556 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SCAN_SQLITEUTILS_H #define SCAN_SQLITEUTILS_H #include #include #include namespace mediascanner { class Statement { public: Statement(sqlite3 *db, const char *sql) { rc = sqlite3_prepare_v2(db, sql, -1, &statement, NULL); if (rc != SQLITE_OK) { throw std::runtime_error(sqlite3_errmsg(db)); } } ~Statement() { try { finalize(); } catch(const std::exception &e) { fprintf(stderr, "Error finalising statement: %s\n", e.what()); } catch(...) { fprintf(stderr, "Unknown error finalising statement.\n"); } } void bind(int pos, int value) { rc = sqlite3_bind_int(statement, pos, value); if (rc != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(rc)); } void bind(int pos, const std::string &value) { rc = sqlite3_bind_text(statement, pos, value.c_str(), value.size(), SQLITE_TRANSIENT); if (rc != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(rc)); } void bind(int pos, void *blob, int length) { rc = sqlite3_bind_blob(statement, pos, blob, length, SQLITE_STATIC); if (rc != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(rc)); } bool step() { rc = sqlite3_step(statement); switch (rc) { case SQLITE_DONE: return false; case SQLITE_ROW: return true; default: throw std::runtime_error(sqlite3_errstr(rc)); } } std::string getText(int column) { if (rc != SQLITE_ROW) throw std::runtime_error("Statement hasn't been executed, or no more results"); return (const char *)sqlite3_column_text(statement, column); } int getInt(int column) { if (rc != SQLITE_ROW) throw std::runtime_error("Statement hasn't been executed, or no more results"); return sqlite3_column_int(statement, column); } void finalize() { if (statement != NULL) { rc = sqlite3_finalize(statement); if (rc != SQLITE_OK) { std::string msg("Could not finalize statement: "); msg += sqlite3_errstr(rc); throw std::runtime_error(msg); } statement = NULL; } } private: sqlite3_stmt *statement; int rc; }; } #endif mediascanner2-0.100+14.04.20140403/src/mediascanner/MediaFile.hh0000644000015301777760000000465512317230414024207 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIAFILE_HH #define MEDIAFILE_HH #include"scannercore.hh" #include namespace mediascanner { class MediaFile final { public: MediaFile(std::string filename) : filename(filename), content_type(""), etag(""), title(""), date(""), author(""), album(""), album_artist(""), track_number(0), duration(0), type(UnknownMedia) {} MediaFile(std::string filename, std::string content_type, std::string etag, std::string title, std::string date, std::string author, std::string album, std::string album_artist, int track_number, int duration, MediaType type); MediaFile() = delete; const std::string& getFileName() const noexcept; const std::string& getContentType() const noexcept; const std::string& getETag() const noexcept; const std::string& getTitle() const noexcept; const std::string& getAuthor() const noexcept; const std::string& getAlbum() const noexcept; const std::string& getAlbumArtist() const noexcept; const std::string& getDate() const noexcept; std::string getUri() const; int getTrackNumber() const noexcept; int getDuration() const noexcept; MediaType getType() const noexcept; bool operator==(const MediaFile &other) const; bool operator!=(const MediaFile &other) const; // There are no setters. MediaFiles are immutable. // For piecewise construction use MediaFileBuilder. private: std::string filename; std::string content_type; std::string etag; std::string title; std::string date; // ISO date string. Should this be time since epoch? std::string author; std::string album; std::string album_artist; int track_number; int duration; // In seconds. MediaType type; }; } #endif mediascanner2-0.100+14.04.20140403/src/mediascanner/MediaFileBuilder.cc0000644000015301777760000000671712317230414025505 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include"MediaFileBuilder.hh" #include"MediaFile.hh" #include namespace mediascanner { MediaFileBuilder::MediaFileBuilder(const std::string &fname) { filename = fname; } MediaFileBuilder::MediaFileBuilder(const MediaFile &mf) : type(mf.getType()), filename(mf.getFileName()), content_type(mf.getContentType()), etag(mf.getETag()), title(mf.getTitle()), date(mf.getDate()), author(mf.getAuthor()), album(mf.getAlbum()), album_artist(mf.getAlbumArtist()), track_number(mf.getTrackNumber()), duration(mf.getDuration()) { } MediaFile MediaFileBuilder::build() const { return MediaFile(filename, content_type, etag, title, date, author, album, album_artist, track_number, duration, type); } void MediaFileBuilder::setType(MediaType t) { if(type_set) throw std::invalid_argument("Tried to set type when it was already set."); type = t; type_set = true; } void MediaFileBuilder::setETag(const std::string &e) { if(etag_set) throw std::invalid_argument("Tried to set filename when it was already set."); etag = e; etag_set = true; } void MediaFileBuilder::setContentType(const std::string &c) { if(content_type_set) throw std::invalid_argument("Tried to set filename when it was already set."); content_type = c; content_type_set = true; } void MediaFileBuilder::setTitle(const std::string &t) { if(title_set) throw std::invalid_argument("Tried to set title when it was already set."); title = t; title_set = true; } void MediaFileBuilder::setDate(const std::string &d) { if(date_set) throw std::invalid_argument("Tried to set date when it was already set."); date = d; date_set = true; } void MediaFileBuilder::setAuthor(const std::string &a) { if(author_set) throw std::invalid_argument("Tried to set author when it was already set."); author = a; author_set = true; } void MediaFileBuilder::setAlbum(const std::string &a) { if(album_set) throw std::invalid_argument("Tried to set album when it was already set."); album = a; album_set = true; } void MediaFileBuilder::setAlbumArtist(const std::string &a) { if(album_artist_set) throw std::invalid_argument("Tried to set album artist when it was already set."); album_artist = a; album_artist_set = true; } void MediaFileBuilder::setTrackNumber(int n) { if(track_number_set) throw std::invalid_argument("Tried to set track number when it was already set."); track_number = n; track_number_set = true; } void MediaFileBuilder::setDuration(int n) { if(duration_set) throw std::invalid_argument("Tried to set duration when it was already set."); duration = n; duration_set = true; } } mediascanner2-0.100+14.04.20140403/src/mediascanner/MediaFileBuilder.hh0000644000015301777760000000530112317230414025503 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIAFILEBUILDER_H_ #define MEDIAFILEBUILDER_H_ #include"scannercore.hh" #include namespace mediascanner { class MediaFile; /** * This is a helper class to build MediaFiles. Since we want MediaFiles * to be immutable and always valid, the user needs to always list * all variables in the constructor. This is cumbersome so this class * allows you to gather them one by one and then finally construct * a fully valid MediaFile. * * If you try to assign the same property twice, an exception is thrown. * This means that MediaFileBuilders are meant to build only one * MediaFile. To build a new one create a new MediaFileBuilder. This is to * ensure that no state leaks from the first MediaFile to the second. */ class MediaFileBuilder final { public: MediaFileBuilder(const std::string &filename); MediaFileBuilder(const MediaFile &mf); MediaFileBuilder(const MediaFileBuilder &) = delete; MediaFileBuilder& operator=(MediaFileBuilder &) = delete; MediaFile build() const; void setType(MediaType t); void setETag(const std::string &e); void setContentType(const std::string &c); void setTitle(const std::string &t); void setDate(const std::string &d); void setAuthor(const std::string &a); void setAlbum(const std::string &a); void setAlbumArtist(const std::string &a); void setTrackNumber(int n); void setDuration(int d); private: bool type_set = false; MediaType type = UnknownMedia; std::string filename; bool content_type_set = false; std::string content_type; bool etag_set = false; std::string etag; bool title_set = false; std::string title; bool date_set = false; std::string date; bool author_set = false; std::string author; bool album_set = false; std::string album; bool album_artist_set = false; std::string album_artist; int track_number = 0; bool track_number_set = false; int duration = 0; bool duration_set = false; }; } #endif mediascanner2-0.100+14.04.20140403/src/mediascanner/mozilla/0000755000015301777760000000000012317231021023477 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/mediascanner/mozilla/Normalize.c0000644000015301777760000025675712317230414025636 0ustar pbusernogroup00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* THIS FILE IS GENERATED BY generate_table.py. DON'T EDIT THIS */ static const unsigned short gNormalizeTable0040[] = { /* U+0040 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, }; static const unsigned short gNormalizeTable0080[] = { /* U+0080 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x0020, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x0020, 0x00a9, 0x0061, 0x00ab, 0x00ac, 0x0020, 0x00ae, 0x0020, 0x00b0, 0x00b1, 0x0032, 0x0033, 0x0020, 0x03bc, 0x00b6, 0x00b7, 0x0020, 0x0031, 0x006f, 0x00bb, 0x0031, 0x0031, 0x0033, 0x00bf, }; static const unsigned short gNormalizeTable00c0[] = { /* U+00c0 */ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x0063, 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, 0x00f0, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x00d7, 0x00f8, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00fe, 0x0073, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x0063, 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, 0x00f0, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x00f7, 0x00f8, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00fe, 0x0079, }; static const unsigned short gNormalizeTable0100[] = { /* U+0100 */ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0064, 0x0064, 0x0111, 0x0111, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0068, 0x0068, 0x0127, 0x0127, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0131, 0x0069, 0x0069, 0x006a, 0x006a, 0x006b, 0x006b, 0x0138, 0x006c, 0x006c, 0x006c, 0x006c, 0x006c, 0x006c, 0x006c, }; static const unsigned short gNormalizeTable0140[] = { /* U+0140 */ 0x006c, 0x0142, 0x0142, 0x006e, 0x006e, 0x006e, 0x006e, 0x006e, 0x006e, 0x02bc, 0x014b, 0x014b, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x0153, 0x0153, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0074, 0x0074, 0x0074, 0x0074, 0x0167, 0x0167, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0077, 0x0077, 0x0079, 0x0079, 0x0079, 0x007a, 0x007a, 0x007a, 0x007a, 0x007a, 0x007a, 0x0073, }; static const unsigned short gNormalizeTable0180[] = { /* U+0180 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, 0x0188, 0x0256, 0x0257, 0x018c, 0x018c, 0x018d, 0x01dd, 0x0259, 0x025b, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, 0x0199, 0x0199, 0x019a, 0x019b, 0x026f, 0x0272, 0x019e, 0x0275, 0x006f, 0x006f, 0x01a3, 0x01a3, 0x01a5, 0x01a5, 0x0280, 0x01a8, 0x01a8, 0x0283, 0x01aa, 0x01ab, 0x01ad, 0x01ad, 0x0288, 0x0075, 0x0075, 0x028a, 0x028b, 0x01b4, 0x01b4, 0x01b6, 0x01b6, 0x0292, 0x01b9, 0x01b9, 0x01ba, 0x01bb, 0x01bd, 0x01bd, 0x01be, 0x01bf, }; static const unsigned short gNormalizeTable01c0[] = { /* U+01c0 */ 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x0064, 0x0064, 0x0064, 0x006c, 0x006c, 0x006c, 0x006e, 0x006e, 0x006e, 0x0061, 0x0061, 0x0069, 0x0069, 0x006f, 0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x01dd, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x00e6, 0x01e5, 0x01e5, 0x0067, 0x0067, 0x006b, 0x006b, 0x006f, 0x006f, 0x006f, 0x006f, 0x0292, 0x0292, 0x006a, 0x0064, 0x0064, 0x0064, 0x0067, 0x0067, 0x0195, 0x01bf, 0x006e, 0x006e, 0x0061, 0x0061, 0x00e6, 0x00e6, 0x00f8, 0x00f8, }; static const unsigned short gNormalizeTable0200[] = { /* U+0200 */ 0x0061, 0x0061, 0x0061, 0x0061, 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, 0x006f, 0x006f, 0x006f, 0x006f, 0x0072, 0x0072, 0x0072, 0x0072, 0x0075, 0x0075, 0x0075, 0x0075, 0x0073, 0x0073, 0x0074, 0x0074, 0x021d, 0x021d, 0x0068, 0x0068, 0x019e, 0x0221, 0x0223, 0x0223, 0x0225, 0x0225, 0x0061, 0x0061, 0x0065, 0x0065, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x0079, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237, 0x0238, 0x0239, 0x2c65, 0x023c, 0x023c, 0x019a, 0x2c66, 0x023f, }; static const unsigned short gNormalizeTable0240[] = { /* U+0240 */ 0x0240, 0x0242, 0x0242, 0x0180, 0x0289, 0x028c, 0x0247, 0x0247, 0x0249, 0x0249, 0x024b, 0x024b, 0x024d, 0x024d, 0x024f, 0x024f, 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, }; static const unsigned short gNormalizeTable0280[] = { /* U+0280 */ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f, 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, 0x0068, 0x0266, 0x006a, 0x0072, 0x0279, 0x027b, 0x0281, 0x0077, 0x0079, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, }; static const unsigned short gNormalizeTable02c0[] = { /* U+02c0 */ 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02de, 0x02df, 0x0263, 0x006c, 0x0073, 0x0078, 0x0295, 0x02e5, 0x02e6, 0x02e7, 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff, }; static const unsigned short gNormalizeTable0340[] = { /* U+0340 */ 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x03b9, 0x0346, 0x0347, 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x0020, 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, 0x0371, 0x0371, 0x0373, 0x0373, 0x02b9, 0x0375, 0x0377, 0x0377, 0x0378, 0x0379, 0x0020, 0x037b, 0x037c, 0x037d, 0x003b, 0x037f, }; static const unsigned short gNormalizeTable0380[] = { /* U+0380 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x0020, 0x03b1, 0x00b7, 0x03b5, 0x03b7, 0x03b9, 0x038b, 0x03bf, 0x038d, 0x03c5, 0x03c9, 0x03b9, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03a2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b9, 0x03c5, 0x03b1, 0x03b5, 0x03b7, 0x03b9, 0x03c5, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, }; static const unsigned short gNormalizeTable03c0[] = { /* U+03c0 */ 0x03c0, 0x03c1, 0x03c3, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b9, 0x03c5, 0x03bf, 0x03c5, 0x03c9, 0x03d7, 0x03b2, 0x03b8, 0x03c5, 0x03c5, 0x03c5, 0x03c6, 0x03c0, 0x03d7, 0x03d9, 0x03d9, 0x03db, 0x03db, 0x03dd, 0x03dd, 0x03df, 0x03df, 0x03e1, 0x03e1, 0x03e3, 0x03e3, 0x03e5, 0x03e5, 0x03e7, 0x03e7, 0x03e9, 0x03e9, 0x03eb, 0x03eb, 0x03ed, 0x03ed, 0x03ef, 0x03ef, 0x03ba, 0x03c1, 0x03c3, 0x03f3, 0x03b8, 0x03b5, 0x03f6, 0x03f8, 0x03f8, 0x03c3, 0x03fb, 0x03fb, 0x03fc, 0x037b, 0x037c, 0x037d, }; static const unsigned short gNormalizeTable0400[] = { /* U+0400 */ 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, 0x0458, 0x0459, 0x045a, 0x045b, 0x043a, 0x0438, 0x0443, 0x045f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0438, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0438, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, }; static const unsigned short gNormalizeTable0440[] = { /* U+0440 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, 0x0458, 0x0459, 0x045a, 0x045b, 0x043a, 0x0438, 0x0443, 0x045f, 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, 0x0469, 0x0469, 0x046b, 0x046b, 0x046d, 0x046d, 0x046f, 0x046f, 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0475, 0x0475, 0x0479, 0x0479, 0x047b, 0x047b, 0x047d, 0x047d, 0x047f, 0x047f, }; static const unsigned short gNormalizeTable0480[] = { /* U+0480 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048b, 0x048b, 0x048d, 0x048d, 0x048f, 0x048f, 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, 0x0499, 0x0499, 0x049b, 0x049b, 0x049d, 0x049d, 0x049f, 0x049f, 0x04a1, 0x04a1, 0x04a3, 0x04a3, 0x04a5, 0x04a5, 0x04a7, 0x04a7, 0x04a9, 0x04a9, 0x04ab, 0x04ab, 0x04ad, 0x04ad, 0x04af, 0x04af, 0x04b1, 0x04b1, 0x04b3, 0x04b3, 0x04b5, 0x04b5, 0x04b7, 0x04b7, 0x04b9, 0x04b9, 0x04bb, 0x04bb, 0x04bd, 0x04bd, 0x04bf, 0x04bf, }; static const unsigned short gNormalizeTable04c0[] = { /* U+04c0 */ 0x04cf, 0x0436, 0x0436, 0x04c4, 0x04c4, 0x04c6, 0x04c6, 0x04c8, 0x04c8, 0x04ca, 0x04ca, 0x04cc, 0x04cc, 0x04ce, 0x04ce, 0x04cf, 0x0430, 0x0430, 0x0430, 0x0430, 0x04d5, 0x04d5, 0x0435, 0x0435, 0x04d9, 0x04d9, 0x04d9, 0x04d9, 0x0436, 0x0436, 0x0437, 0x0437, 0x04e1, 0x04e1, 0x0438, 0x0438, 0x0438, 0x0438, 0x043e, 0x043e, 0x04e9, 0x04e9, 0x04e9, 0x04e9, 0x044d, 0x044d, 0x0443, 0x0443, 0x0443, 0x0443, 0x0443, 0x0443, 0x0447, 0x0447, 0x04f7, 0x04f7, 0x044b, 0x044b, 0x04fb, 0x04fb, 0x04fd, 0x04fd, 0x04ff, 0x04ff, }; static const unsigned short gNormalizeTable0500[] = { /* U+0500 */ 0x0501, 0x0501, 0x0503, 0x0503, 0x0505, 0x0505, 0x0507, 0x0507, 0x0509, 0x0509, 0x050b, 0x050b, 0x050d, 0x050d, 0x050f, 0x050f, 0x0511, 0x0511, 0x0513, 0x0513, 0x0515, 0x0515, 0x0517, 0x0517, 0x0519, 0x0519, 0x051b, 0x051b, 0x051d, 0x051d, 0x051f, 0x051f, 0x0521, 0x0521, 0x0523, 0x0523, 0x0525, 0x0525, 0x0526, 0x0527, 0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f, 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f, }; static const unsigned short gNormalizeTable0540[] = { /* U+0540 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, 0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f, 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f, 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f, }; static const unsigned short gNormalizeTable0580[] = { /* U+0580 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0565, 0x0588, 0x0589, 0x058a, 0x058b, 0x058c, 0x058d, 0x058e, 0x058f, 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059a, 0x059b, 0x059c, 0x059d, 0x059e, 0x059f, 0x05a0, 0x05a1, 0x05a2, 0x05a3, 0x05a4, 0x05a5, 0x05a6, 0x05a7, 0x05a8, 0x05a9, 0x05aa, 0x05ab, 0x05ac, 0x05ad, 0x05ae, 0x05af, 0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05ba, 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf, }; static const unsigned short gNormalizeTable0600[] = { /* U+0600 */ 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, 0x0608, 0x0609, 0x060a, 0x060b, 0x060c, 0x060d, 0x060e, 0x060f, 0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, 0x0618, 0x0619, 0x061a, 0x061b, 0x061c, 0x061d, 0x061e, 0x061f, 0x0620, 0x0621, 0x0627, 0x0627, 0x0648, 0x0627, 0x064a, 0x0627, 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063a, 0x063b, 0x063c, 0x063d, 0x063e, 0x063f, }; static const unsigned short gNormalizeTable0640[] = { /* U+0640 */ 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657, 0x0658, 0x0659, 0x065a, 0x065b, 0x065c, 0x065d, 0x065e, 0x065f, 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0x066a, 0x066b, 0x066c, 0x066d, 0x066e, 0x066f, 0x0670, 0x0671, 0x0672, 0x0673, 0x0674, 0x0627, 0x0648, 0x06c7, 0x064a, 0x0679, 0x067a, 0x067b, 0x067c, 0x067d, 0x067e, 0x067f, }; static const unsigned short gNormalizeTable06c0[] = { /* U+06c0 */ 0x06d5, 0x06c1, 0x06c1, 0x06c3, 0x06c4, 0x06c5, 0x06c6, 0x06c7, 0x06c8, 0x06c9, 0x06ca, 0x06cb, 0x06cc, 0x06cd, 0x06ce, 0x06cf, 0x06d0, 0x06d1, 0x06d2, 0x06d2, 0x06d4, 0x06d5, 0x06d6, 0x06d7, 0x06d8, 0x06d9, 0x06da, 0x06db, 0x06dc, 0x06dd, 0x06de, 0x06df, 0x06e0, 0x06e1, 0x06e2, 0x06e3, 0x06e4, 0x06e5, 0x06e6, 0x06e7, 0x06e8, 0x06e9, 0x06ea, 0x06eb, 0x06ec, 0x06ed, 0x06ee, 0x06ef, 0x06f0, 0x06f1, 0x06f2, 0x06f3, 0x06f4, 0x06f5, 0x06f6, 0x06f7, 0x06f8, 0x06f9, 0x06fa, 0x06fb, 0x06fc, 0x06fd, 0x06fe, 0x06ff, }; static const unsigned short gNormalizeTable0900[] = { /* U+0900 */ 0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, 0x0908, 0x0909, 0x090a, 0x090b, 0x090c, 0x090d, 0x090e, 0x090f, 0x0910, 0x0911, 0x0912, 0x0913, 0x0914, 0x0915, 0x0916, 0x0917, 0x0918, 0x0919, 0x091a, 0x091b, 0x091c, 0x091d, 0x091e, 0x091f, 0x0920, 0x0921, 0x0922, 0x0923, 0x0924, 0x0925, 0x0926, 0x0927, 0x0928, 0x0928, 0x092a, 0x092b, 0x092c, 0x092d, 0x092e, 0x092f, 0x0930, 0x0930, 0x0932, 0x0933, 0x0933, 0x0935, 0x0936, 0x0937, 0x0938, 0x0939, 0x093a, 0x093b, 0x093c, 0x093d, 0x093e, 0x093f, }; static const unsigned short gNormalizeTable0940[] = { /* U+0940 */ 0x0940, 0x0941, 0x0942, 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, 0x0948, 0x0949, 0x094a, 0x094b, 0x094c, 0x094d, 0x094e, 0x094f, 0x0950, 0x0951, 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957, 0x0915, 0x0916, 0x0917, 0x091c, 0x0921, 0x0922, 0x092b, 0x092f, 0x0960, 0x0961, 0x0962, 0x0963, 0x0964, 0x0965, 0x0966, 0x0967, 0x0968, 0x0969, 0x096a, 0x096b, 0x096c, 0x096d, 0x096e, 0x096f, 0x0970, 0x0971, 0x0972, 0x0973, 0x0974, 0x0975, 0x0976, 0x0977, 0x0978, 0x0979, 0x097a, 0x097b, 0x097c, 0x097d, 0x097e, 0x097f, }; static const unsigned short gNormalizeTable09c0[] = { /* U+09c0 */ 0x09c0, 0x09c1, 0x09c2, 0x09c3, 0x09c4, 0x09c5, 0x09c6, 0x09c7, 0x09c8, 0x09c9, 0x09ca, 0x09c7, 0x09c7, 0x09cd, 0x09ce, 0x09cf, 0x09d0, 0x09d1, 0x09d2, 0x09d3, 0x09d4, 0x09d5, 0x09d6, 0x09d7, 0x09d8, 0x09d9, 0x09da, 0x09db, 0x09a1, 0x09a2, 0x09de, 0x09af, 0x09e0, 0x09e1, 0x09e2, 0x09e3, 0x09e4, 0x09e5, 0x09e6, 0x09e7, 0x09e8, 0x09e9, 0x09ea, 0x09eb, 0x09ec, 0x09ed, 0x09ee, 0x09ef, 0x09f0, 0x09f1, 0x09f2, 0x09f3, 0x09f4, 0x09f5, 0x09f6, 0x09f7, 0x09f8, 0x09f9, 0x09fa, 0x09fb, 0x09fc, 0x09fd, 0x09fe, 0x09ff, }; static const unsigned short gNormalizeTable0a00[] = { /* U+0a00 */ 0x0a00, 0x0a01, 0x0a02, 0x0a03, 0x0a04, 0x0a05, 0x0a06, 0x0a07, 0x0a08, 0x0a09, 0x0a0a, 0x0a0b, 0x0a0c, 0x0a0d, 0x0a0e, 0x0a0f, 0x0a10, 0x0a11, 0x0a12, 0x0a13, 0x0a14, 0x0a15, 0x0a16, 0x0a17, 0x0a18, 0x0a19, 0x0a1a, 0x0a1b, 0x0a1c, 0x0a1d, 0x0a1e, 0x0a1f, 0x0a20, 0x0a21, 0x0a22, 0x0a23, 0x0a24, 0x0a25, 0x0a26, 0x0a27, 0x0a28, 0x0a29, 0x0a2a, 0x0a2b, 0x0a2c, 0x0a2d, 0x0a2e, 0x0a2f, 0x0a30, 0x0a31, 0x0a32, 0x0a32, 0x0a34, 0x0a35, 0x0a38, 0x0a37, 0x0a38, 0x0a39, 0x0a3a, 0x0a3b, 0x0a3c, 0x0a3d, 0x0a3e, 0x0a3f, }; static const unsigned short gNormalizeTable0a40[] = { /* U+0a40 */ 0x0a40, 0x0a41, 0x0a42, 0x0a43, 0x0a44, 0x0a45, 0x0a46, 0x0a47, 0x0a48, 0x0a49, 0x0a4a, 0x0a4b, 0x0a4c, 0x0a4d, 0x0a4e, 0x0a4f, 0x0a50, 0x0a51, 0x0a52, 0x0a53, 0x0a54, 0x0a55, 0x0a56, 0x0a57, 0x0a58, 0x0a16, 0x0a17, 0x0a1c, 0x0a5c, 0x0a5d, 0x0a2b, 0x0a5f, 0x0a60, 0x0a61, 0x0a62, 0x0a63, 0x0a64, 0x0a65, 0x0a66, 0x0a67, 0x0a68, 0x0a69, 0x0a6a, 0x0a6b, 0x0a6c, 0x0a6d, 0x0a6e, 0x0a6f, 0x0a70, 0x0a71, 0x0a72, 0x0a73, 0x0a74, 0x0a75, 0x0a76, 0x0a77, 0x0a78, 0x0a79, 0x0a7a, 0x0a7b, 0x0a7c, 0x0a7d, 0x0a7e, 0x0a7f, }; static const unsigned short gNormalizeTable0b40[] = { /* U+0b40 */ 0x0b40, 0x0b41, 0x0b42, 0x0b43, 0x0b44, 0x0b45, 0x0b46, 0x0b47, 0x0b47, 0x0b49, 0x0b4a, 0x0b47, 0x0b47, 0x0b4d, 0x0b4e, 0x0b4f, 0x0b50, 0x0b51, 0x0b52, 0x0b53, 0x0b54, 0x0b55, 0x0b56, 0x0b57, 0x0b58, 0x0b59, 0x0b5a, 0x0b5b, 0x0b21, 0x0b22, 0x0b5e, 0x0b5f, 0x0b60, 0x0b61, 0x0b62, 0x0b63, 0x0b64, 0x0b65, 0x0b66, 0x0b67, 0x0b68, 0x0b69, 0x0b6a, 0x0b6b, 0x0b6c, 0x0b6d, 0x0b6e, 0x0b6f, 0x0b70, 0x0b71, 0x0b72, 0x0b73, 0x0b74, 0x0b75, 0x0b76, 0x0b77, 0x0b78, 0x0b79, 0x0b7a, 0x0b7b, 0x0b7c, 0x0b7d, 0x0b7e, 0x0b7f, }; static const unsigned short gNormalizeTable0b80[] = { /* U+0b80 */ 0x0b80, 0x0b81, 0x0b82, 0x0b83, 0x0b84, 0x0b85, 0x0b86, 0x0b87, 0x0b88, 0x0b89, 0x0b8a, 0x0b8b, 0x0b8c, 0x0b8d, 0x0b8e, 0x0b8f, 0x0b90, 0x0b91, 0x0b92, 0x0b93, 0x0b92, 0x0b95, 0x0b96, 0x0b97, 0x0b98, 0x0b99, 0x0b9a, 0x0b9b, 0x0b9c, 0x0b9d, 0x0b9e, 0x0b9f, 0x0ba0, 0x0ba1, 0x0ba2, 0x0ba3, 0x0ba4, 0x0ba5, 0x0ba6, 0x0ba7, 0x0ba8, 0x0ba9, 0x0baa, 0x0bab, 0x0bac, 0x0bad, 0x0bae, 0x0baf, 0x0bb0, 0x0bb1, 0x0bb2, 0x0bb3, 0x0bb4, 0x0bb5, 0x0bb6, 0x0bb7, 0x0bb8, 0x0bb9, 0x0bba, 0x0bbb, 0x0bbc, 0x0bbd, 0x0bbe, 0x0bbf, }; static const unsigned short gNormalizeTable0bc0[] = { /* U+0bc0 */ 0x0bc0, 0x0bc1, 0x0bc2, 0x0bc3, 0x0bc4, 0x0bc5, 0x0bc6, 0x0bc7, 0x0bc8, 0x0bc9, 0x0bc6, 0x0bc7, 0x0bc6, 0x0bcd, 0x0bce, 0x0bcf, 0x0bd0, 0x0bd1, 0x0bd2, 0x0bd3, 0x0bd4, 0x0bd5, 0x0bd6, 0x0bd7, 0x0bd8, 0x0bd9, 0x0bda, 0x0bdb, 0x0bdc, 0x0bdd, 0x0bde, 0x0bdf, 0x0be0, 0x0be1, 0x0be2, 0x0be3, 0x0be4, 0x0be5, 0x0be6, 0x0be7, 0x0be8, 0x0be9, 0x0bea, 0x0beb, 0x0bec, 0x0bed, 0x0bee, 0x0bef, 0x0bf0, 0x0bf1, 0x0bf2, 0x0bf3, 0x0bf4, 0x0bf5, 0x0bf6, 0x0bf7, 0x0bf8, 0x0bf9, 0x0bfa, 0x0bfb, 0x0bfc, 0x0bfd, 0x0bfe, 0x0bff, }; static const unsigned short gNormalizeTable0c40[] = { /* U+0c40 */ 0x0c40, 0x0c41, 0x0c42, 0x0c43, 0x0c44, 0x0c45, 0x0c46, 0x0c47, 0x0c46, 0x0c49, 0x0c4a, 0x0c4b, 0x0c4c, 0x0c4d, 0x0c4e, 0x0c4f, 0x0c50, 0x0c51, 0x0c52, 0x0c53, 0x0c54, 0x0c55, 0x0c56, 0x0c57, 0x0c58, 0x0c59, 0x0c5a, 0x0c5b, 0x0c5c, 0x0c5d, 0x0c5e, 0x0c5f, 0x0c60, 0x0c61, 0x0c62, 0x0c63, 0x0c64, 0x0c65, 0x0c66, 0x0c67, 0x0c68, 0x0c69, 0x0c6a, 0x0c6b, 0x0c6c, 0x0c6d, 0x0c6e, 0x0c6f, 0x0c70, 0x0c71, 0x0c72, 0x0c73, 0x0c74, 0x0c75, 0x0c76, 0x0c77, 0x0c78, 0x0c79, 0x0c7a, 0x0c7b, 0x0c7c, 0x0c7d, 0x0c7e, 0x0c7f, }; static const unsigned short gNormalizeTable0cc0[] = { /* U+0cc0 */ 0x0cbf, 0x0cc1, 0x0cc2, 0x0cc3, 0x0cc4, 0x0cc5, 0x0cc6, 0x0cc6, 0x0cc6, 0x0cc9, 0x0cc6, 0x0cc6, 0x0ccc, 0x0ccd, 0x0cce, 0x0ccf, 0x0cd0, 0x0cd1, 0x0cd2, 0x0cd3, 0x0cd4, 0x0cd5, 0x0cd6, 0x0cd7, 0x0cd8, 0x0cd9, 0x0cda, 0x0cdb, 0x0cdc, 0x0cdd, 0x0cde, 0x0cdf, 0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x0cef, 0x0cf0, 0x0cf1, 0x0cf2, 0x0cf3, 0x0cf4, 0x0cf5, 0x0cf6, 0x0cf7, 0x0cf8, 0x0cf9, 0x0cfa, 0x0cfb, 0x0cfc, 0x0cfd, 0x0cfe, 0x0cff, }; static const unsigned short gNormalizeTable0d40[] = { /* U+0d40 */ 0x0d40, 0x0d41, 0x0d42, 0x0d43, 0x0d44, 0x0d45, 0x0d46, 0x0d47, 0x0d48, 0x0d49, 0x0d46, 0x0d47, 0x0d46, 0x0d4d, 0x0d4e, 0x0d4f, 0x0d50, 0x0d51, 0x0d52, 0x0d53, 0x0d54, 0x0d55, 0x0d56, 0x0d57, 0x0d58, 0x0d59, 0x0d5a, 0x0d5b, 0x0d5c, 0x0d5d, 0x0d5e, 0x0d5f, 0x0d60, 0x0d61, 0x0d62, 0x0d63, 0x0d64, 0x0d65, 0x0d66, 0x0d67, 0x0d68, 0x0d69, 0x0d6a, 0x0d6b, 0x0d6c, 0x0d6d, 0x0d6e, 0x0d6f, 0x0d70, 0x0d71, 0x0d72, 0x0d73, 0x0d74, 0x0d75, 0x0d76, 0x0d77, 0x0d78, 0x0d79, 0x0d7a, 0x0d7b, 0x0d7c, 0x0d7d, 0x0d7e, 0x0d7f, }; static const unsigned short gNormalizeTable0dc0[] = { /* U+0dc0 */ 0x0dc0, 0x0dc1, 0x0dc2, 0x0dc3, 0x0dc4, 0x0dc5, 0x0dc6, 0x0dc7, 0x0dc8, 0x0dc9, 0x0dca, 0x0dcb, 0x0dcc, 0x0dcd, 0x0dce, 0x0dcf, 0x0dd0, 0x0dd1, 0x0dd2, 0x0dd3, 0x0dd4, 0x0dd5, 0x0dd6, 0x0dd7, 0x0dd8, 0x0dd9, 0x0dd9, 0x0ddb, 0x0dd9, 0x0dd9, 0x0dd9, 0x0ddf, 0x0de0, 0x0de1, 0x0de2, 0x0de3, 0x0de4, 0x0de5, 0x0de6, 0x0de7, 0x0de8, 0x0de9, 0x0dea, 0x0deb, 0x0dec, 0x0ded, 0x0dee, 0x0def, 0x0df0, 0x0df1, 0x0df2, 0x0df3, 0x0df4, 0x0df5, 0x0df6, 0x0df7, 0x0df8, 0x0df9, 0x0dfa, 0x0dfb, 0x0dfc, 0x0dfd, 0x0dfe, 0x0dff, }; static const unsigned short gNormalizeTable0e00[] = { /* U+0e00 */ 0x0e00, 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, 0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, 0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, 0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, 0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, 0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e30, 0x0e31, 0x0e32, 0x0e4d, 0x0e34, 0x0e35, 0x0e36, 0x0e37, 0x0e38, 0x0e39, 0x0e3a, 0x0e3b, 0x0e3c, 0x0e3d, 0x0e3e, 0x0e3f, }; static const unsigned short gNormalizeTable0e80[] = { /* U+0e80 */ 0x0e80, 0x0e81, 0x0e82, 0x0e83, 0x0e84, 0x0e85, 0x0e86, 0x0e87, 0x0e88, 0x0e89, 0x0e8a, 0x0e8b, 0x0e8c, 0x0e8d, 0x0e8e, 0x0e8f, 0x0e90, 0x0e91, 0x0e92, 0x0e93, 0x0e94, 0x0e95, 0x0e96, 0x0e97, 0x0e98, 0x0e99, 0x0e9a, 0x0e9b, 0x0e9c, 0x0e9d, 0x0e9e, 0x0e9f, 0x0ea0, 0x0ea1, 0x0ea2, 0x0ea3, 0x0ea4, 0x0ea5, 0x0ea6, 0x0ea7, 0x0ea8, 0x0ea9, 0x0eaa, 0x0eab, 0x0eac, 0x0ead, 0x0eae, 0x0eaf, 0x0eb0, 0x0eb1, 0x0eb2, 0x0ecd, 0x0eb4, 0x0eb5, 0x0eb6, 0x0eb7, 0x0eb8, 0x0eb9, 0x0eba, 0x0ebb, 0x0ebc, 0x0ebd, 0x0ebe, 0x0ebf, }; static const unsigned short gNormalizeTable0ec0[] = { /* U+0ec0 */ 0x0ec0, 0x0ec1, 0x0ec2, 0x0ec3, 0x0ec4, 0x0ec5, 0x0ec6, 0x0ec7, 0x0ec8, 0x0ec9, 0x0eca, 0x0ecb, 0x0ecc, 0x0ecd, 0x0ece, 0x0ecf, 0x0ed0, 0x0ed1, 0x0ed2, 0x0ed3, 0x0ed4, 0x0ed5, 0x0ed6, 0x0ed7, 0x0ed8, 0x0ed9, 0x0eda, 0x0edb, 0x0eab, 0x0eab, 0x0ede, 0x0edf, 0x0ee0, 0x0ee1, 0x0ee2, 0x0ee3, 0x0ee4, 0x0ee5, 0x0ee6, 0x0ee7, 0x0ee8, 0x0ee9, 0x0eea, 0x0eeb, 0x0eec, 0x0eed, 0x0eee, 0x0eef, 0x0ef0, 0x0ef1, 0x0ef2, 0x0ef3, 0x0ef4, 0x0ef5, 0x0ef6, 0x0ef7, 0x0ef8, 0x0ef9, 0x0efa, 0x0efb, 0x0efc, 0x0efd, 0x0efe, 0x0eff, }; static const unsigned short gNormalizeTable0f00[] = { /* U+0f00 */ 0x0f00, 0x0f01, 0x0f02, 0x0f03, 0x0f04, 0x0f05, 0x0f06, 0x0f07, 0x0f08, 0x0f09, 0x0f0a, 0x0f0b, 0x0f0b, 0x0f0d, 0x0f0e, 0x0f0f, 0x0f10, 0x0f11, 0x0f12, 0x0f13, 0x0f14, 0x0f15, 0x0f16, 0x0f17, 0x0f18, 0x0f19, 0x0f1a, 0x0f1b, 0x0f1c, 0x0f1d, 0x0f1e, 0x0f1f, 0x0f20, 0x0f21, 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29, 0x0f2a, 0x0f2b, 0x0f2c, 0x0f2d, 0x0f2e, 0x0f2f, 0x0f30, 0x0f31, 0x0f32, 0x0f33, 0x0f34, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3a, 0x0f3b, 0x0f3c, 0x0f3d, 0x0f3e, 0x0f3f, }; static const unsigned short gNormalizeTable0f40[] = { /* U+0f40 */ 0x0f40, 0x0f41, 0x0f42, 0x0f42, 0x0f44, 0x0f45, 0x0f46, 0x0f47, 0x0f48, 0x0f49, 0x0f4a, 0x0f4b, 0x0f4c, 0x0f4c, 0x0f4e, 0x0f4f, 0x0f50, 0x0f51, 0x0f51, 0x0f53, 0x0f54, 0x0f55, 0x0f56, 0x0f56, 0x0f58, 0x0f59, 0x0f5a, 0x0f5b, 0x0f5b, 0x0f5d, 0x0f5e, 0x0f5f, 0x0f60, 0x0f61, 0x0f62, 0x0f63, 0x0f64, 0x0f65, 0x0f66, 0x0f67, 0x0f68, 0x0f40, 0x0f6a, 0x0f6b, 0x0f6c, 0x0f6d, 0x0f6e, 0x0f6f, 0x0f70, 0x0f71, 0x0f72, 0x0f71, 0x0f74, 0x0f71, 0x0fb2, 0x0fb2, 0x0fb3, 0x0fb3, 0x0f7a, 0x0f7b, 0x0f7c, 0x0f7d, 0x0f7e, 0x0f7f, }; static const unsigned short gNormalizeTable0f80[] = { /* U+0f80 */ 0x0f80, 0x0f71, 0x0f82, 0x0f83, 0x0f84, 0x0f85, 0x0f86, 0x0f87, 0x0f88, 0x0f89, 0x0f8a, 0x0f8b, 0x0f8c, 0x0f8d, 0x0f8e, 0x0f8f, 0x0f90, 0x0f91, 0x0f92, 0x0f92, 0x0f94, 0x0f95, 0x0f96, 0x0f97, 0x0f98, 0x0f99, 0x0f9a, 0x0f9b, 0x0f9c, 0x0f9c, 0x0f9e, 0x0f9f, 0x0fa0, 0x0fa1, 0x0fa1, 0x0fa3, 0x0fa4, 0x0fa5, 0x0fa6, 0x0fa6, 0x0fa8, 0x0fa9, 0x0faa, 0x0fab, 0x0fab, 0x0fad, 0x0fae, 0x0faf, 0x0fb0, 0x0fb1, 0x0fb2, 0x0fb3, 0x0fb4, 0x0fb5, 0x0fb6, 0x0fb7, 0x0fb8, 0x0f90, 0x0fba, 0x0fbb, 0x0fbc, 0x0fbd, 0x0fbe, 0x0fbf, }; static const unsigned short gNormalizeTable1000[] = { /* U+1000 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x1009, 0x100a, 0x100b, 0x100c, 0x100d, 0x100e, 0x100f, 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, 0x1018, 0x1019, 0x101a, 0x101b, 0x101c, 0x101d, 0x101e, 0x101f, 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1025, 0x1027, 0x1028, 0x1029, 0x102a, 0x102b, 0x102c, 0x102d, 0x102e, 0x102f, 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, 0x1038, 0x1039, 0x103a, 0x103b, 0x103c, 0x103d, 0x103e, 0x103f, }; static const unsigned short gNormalizeTable1080[] = { /* U+1080 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, 0x1088, 0x1089, 0x108a, 0x108b, 0x108c, 0x108d, 0x108e, 0x108f, 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, 0x1098, 0x1099, 0x109a, 0x109b, 0x109c, 0x109d, 0x109e, 0x109f, 0x2d00, 0x2d01, 0x2d02, 0x2d03, 0x2d04, 0x2d05, 0x2d06, 0x2d07, 0x2d08, 0x2d09, 0x2d0a, 0x2d0b, 0x2d0c, 0x2d0d, 0x2d0e, 0x2d0f, 0x2d10, 0x2d11, 0x2d12, 0x2d13, 0x2d14, 0x2d15, 0x2d16, 0x2d17, 0x2d18, 0x2d19, 0x2d1a, 0x2d1b, 0x2d1c, 0x2d1d, 0x2d1e, 0x2d1f, }; static const unsigned short gNormalizeTable10c0[] = { /* U+10c0 */ 0x2d20, 0x2d21, 0x2d22, 0x2d23, 0x2d24, 0x2d25, 0x10c6, 0x10c7, 0x10c8, 0x10c9, 0x10ca, 0x10cb, 0x10cc, 0x10cd, 0x10ce, 0x10cf, 0x10d0, 0x10d1, 0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7, 0x10d8, 0x10d9, 0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df, 0x10e0, 0x10e1, 0x10e2, 0x10e3, 0x10e4, 0x10e5, 0x10e6, 0x10e7, 0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed, 0x10ee, 0x10ef, 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x10f8, 0x10f9, 0x10fa, 0x10fb, 0x10dc, 0x10fd, 0x10fe, 0x10ff, }; static const unsigned short gNormalizeTable1140[] = { /* U+1140 */ 0x1140, 0x1141, 0x1142, 0x1143, 0x1144, 0x1145, 0x1146, 0x1147, 0x1148, 0x1149, 0x114a, 0x114b, 0x114c, 0x114d, 0x114e, 0x114f, 0x1150, 0x1151, 0x1152, 0x1153, 0x1154, 0x1155, 0x1156, 0x1157, 0x1158, 0x1159, 0x115a, 0x115b, 0x115c, 0x115d, 0x115e, 0x0020, 0x0020, 0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, 0x1168, 0x1169, 0x116a, 0x116b, 0x116c, 0x116d, 0x116e, 0x116f, 0x1170, 0x1171, 0x1172, 0x1173, 0x1174, 0x1175, 0x1176, 0x1177, 0x1178, 0x1179, 0x117a, 0x117b, 0x117c, 0x117d, 0x117e, 0x117f, }; static const unsigned short gNormalizeTable1780[] = { /* U+1780 */ 0x1780, 0x1781, 0x1782, 0x1783, 0x1784, 0x1785, 0x1786, 0x1787, 0x1788, 0x1789, 0x178a, 0x178b, 0x178c, 0x178d, 0x178e, 0x178f, 0x1790, 0x1791, 0x1792, 0x1793, 0x1794, 0x1795, 0x1796, 0x1797, 0x1798, 0x1799, 0x179a, 0x179b, 0x179c, 0x179d, 0x179e, 0x179f, 0x17a0, 0x17a1, 0x17a2, 0x17a3, 0x17a4, 0x17a5, 0x17a6, 0x17a7, 0x17a8, 0x17a9, 0x17aa, 0x17ab, 0x17ac, 0x17ad, 0x17ae, 0x17af, 0x17b0, 0x17b1, 0x17b2, 0x17b3, 0x0020, 0x0020, 0x17b6, 0x17b7, 0x17b8, 0x17b9, 0x17ba, 0x17bb, 0x17bc, 0x17bd, 0x17be, 0x17bf, }; static const unsigned short gNormalizeTable1800[] = { /* U+1800 */ 0x1800, 0x1801, 0x1802, 0x1803, 0x1804, 0x1805, 0x1806, 0x1807, 0x1808, 0x1809, 0x180a, 0x0020, 0x0020, 0x0020, 0x180e, 0x180f, 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819, 0x181a, 0x181b, 0x181c, 0x181d, 0x181e, 0x181f, 0x1820, 0x1821, 0x1822, 0x1823, 0x1824, 0x1825, 0x1826, 0x1827, 0x1828, 0x1829, 0x182a, 0x182b, 0x182c, 0x182d, 0x182e, 0x182f, 0x1830, 0x1831, 0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837, 0x1838, 0x1839, 0x183a, 0x183b, 0x183c, 0x183d, 0x183e, 0x183f, }; static const unsigned short gNormalizeTable1b00[] = { /* U+1b00 */ 0x1b00, 0x1b01, 0x1b02, 0x1b03, 0x1b04, 0x1b05, 0x1b05, 0x1b07, 0x1b07, 0x1b09, 0x1b09, 0x1b0b, 0x1b0b, 0x1b0d, 0x1b0d, 0x1b0f, 0x1b10, 0x1b11, 0x1b11, 0x1b13, 0x1b14, 0x1b15, 0x1b16, 0x1b17, 0x1b18, 0x1b19, 0x1b1a, 0x1b1b, 0x1b1c, 0x1b1d, 0x1b1e, 0x1b1f, 0x1b20, 0x1b21, 0x1b22, 0x1b23, 0x1b24, 0x1b25, 0x1b26, 0x1b27, 0x1b28, 0x1b29, 0x1b2a, 0x1b2b, 0x1b2c, 0x1b2d, 0x1b2e, 0x1b2f, 0x1b30, 0x1b31, 0x1b32, 0x1b33, 0x1b34, 0x1b35, 0x1b36, 0x1b37, 0x1b38, 0x1b39, 0x1b3a, 0x1b3a, 0x1b3c, 0x1b3c, 0x1b3e, 0x1b3f, }; static const unsigned short gNormalizeTable1b40[] = { /* U+1b40 */ 0x1b3e, 0x1b3f, 0x1b42, 0x1b42, 0x1b44, 0x1b45, 0x1b46, 0x1b47, 0x1b48, 0x1b49, 0x1b4a, 0x1b4b, 0x1b4c, 0x1b4d, 0x1b4e, 0x1b4f, 0x1b50, 0x1b51, 0x1b52, 0x1b53, 0x1b54, 0x1b55, 0x1b56, 0x1b57, 0x1b58, 0x1b59, 0x1b5a, 0x1b5b, 0x1b5c, 0x1b5d, 0x1b5e, 0x1b5f, 0x1b60, 0x1b61, 0x1b62, 0x1b63, 0x1b64, 0x1b65, 0x1b66, 0x1b67, 0x1b68, 0x1b69, 0x1b6a, 0x1b6b, 0x1b6c, 0x1b6d, 0x1b6e, 0x1b6f, 0x1b70, 0x1b71, 0x1b72, 0x1b73, 0x1b74, 0x1b75, 0x1b76, 0x1b77, 0x1b78, 0x1b79, 0x1b7a, 0x1b7b, 0x1b7c, 0x1b7d, 0x1b7e, 0x1b7f, }; static const unsigned short gNormalizeTable1d00[] = { /* U+1d00 */ 0x1d00, 0x1d01, 0x1d02, 0x1d03, 0x1d04, 0x1d05, 0x1d06, 0x1d07, 0x1d08, 0x1d09, 0x1d0a, 0x1d0b, 0x1d0c, 0x1d0d, 0x1d0e, 0x1d0f, 0x1d10, 0x1d11, 0x1d12, 0x1d13, 0x1d14, 0x1d15, 0x1d16, 0x1d17, 0x1d18, 0x1d19, 0x1d1a, 0x1d1b, 0x1d1c, 0x1d1d, 0x1d1e, 0x1d1f, 0x1d20, 0x1d21, 0x1d22, 0x1d23, 0x1d24, 0x1d25, 0x1d26, 0x1d27, 0x1d28, 0x1d29, 0x1d2a, 0x1d2b, 0x0061, 0x00e6, 0x0062, 0x1d2f, 0x0064, 0x0065, 0x01dd, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x1d3b, 0x006f, 0x0223, 0x0070, 0x0072, }; static const unsigned short gNormalizeTable1d40[] = { /* U+1d40 */ 0x0074, 0x0075, 0x0077, 0x0061, 0x0250, 0x0251, 0x1d02, 0x0062, 0x0064, 0x0065, 0x0259, 0x025b, 0x025c, 0x0067, 0x1d4e, 0x006b, 0x006d, 0x014b, 0x006f, 0x0254, 0x1d16, 0x1d17, 0x0070, 0x0074, 0x0075, 0x1d1d, 0x026f, 0x0076, 0x1d25, 0x03b2, 0x03b3, 0x03b4, 0x03c6, 0x03c7, 0x0069, 0x0072, 0x0075, 0x0076, 0x03b2, 0x03b3, 0x03c1, 0x03c6, 0x03c7, 0x1d6b, 0x1d6c, 0x1d6d, 0x1d6e, 0x1d6f, 0x1d70, 0x1d71, 0x1d72, 0x1d73, 0x1d74, 0x1d75, 0x1d76, 0x1d77, 0x043d, 0x1d79, 0x1d7a, 0x1d7b, 0x1d7c, 0x1d7d, 0x1d7e, 0x1d7f, }; static const unsigned short gNormalizeTable1d80[] = { /* U+1d80 */ 0x1d80, 0x1d81, 0x1d82, 0x1d83, 0x1d84, 0x1d85, 0x1d86, 0x1d87, 0x1d88, 0x1d89, 0x1d8a, 0x1d8b, 0x1d8c, 0x1d8d, 0x1d8e, 0x1d8f, 0x1d90, 0x1d91, 0x1d92, 0x1d93, 0x1d94, 0x1d95, 0x1d96, 0x1d97, 0x1d98, 0x1d99, 0x1d9a, 0x0252, 0x0063, 0x0255, 0x00f0, 0x025c, 0x0066, 0x025f, 0x0261, 0x0265, 0x0268, 0x0269, 0x026a, 0x1d7b, 0x029d, 0x026d, 0x1d85, 0x029f, 0x0271, 0x0270, 0x0272, 0x0273, 0x0274, 0x0275, 0x0278, 0x0282, 0x0283, 0x01ab, 0x0289, 0x028a, 0x1d1c, 0x028b, 0x028c, 0x007a, 0x0290, 0x0291, 0x0292, 0x03b8, }; static const unsigned short gNormalizeTable1e00[] = { /* U+1e00 */ 0x0061, 0x0061, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0063, 0x0063, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0066, 0x0066, 0x0067, 0x0067, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0069, 0x0069, 0x0069, 0x0069, 0x006b, 0x006b, 0x006b, 0x006b, 0x006b, 0x006b, 0x006c, 0x006c, 0x006c, 0x006c, 0x006c, 0x006c, 0x006c, 0x006c, 0x006d, 0x006d, }; static const unsigned short gNormalizeTable1e40[] = { /* U+1e40 */ 0x006d, 0x006d, 0x006d, 0x006d, 0x006e, 0x006e, 0x006e, 0x006e, 0x006e, 0x006e, 0x006e, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x0070, 0x0070, 0x0070, 0x0070, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0074, 0x0074, 0x0074, 0x0074, 0x0074, 0x0074, 0x0074, 0x0074, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0076, 0x0076, 0x0076, 0x0076, }; static const unsigned short gNormalizeTable1e80[] = { /* U+1e80 */ 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0078, 0x0078, 0x0078, 0x0078, 0x0079, 0x0079, 0x007a, 0x007a, 0x007a, 0x007a, 0x007a, 0x007a, 0x0068, 0x0074, 0x0077, 0x0079, 0x0061, 0x0073, 0x1e9c, 0x1e9d, 0x0073, 0x1e9f, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, }; static const unsigned short gNormalizeTable1ec0[] = { /* U+1ec0 */ 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x0079, 0x0079, 0x0079, 0x0079, 0x0079, 0x0079, 0x0079, 0x1efb, 0x1efb, 0x1efd, 0x1efd, 0x1eff, 0x1eff, }; static const unsigned short gNormalizeTable1f00[] = { /* U+1f00 */ 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b5, 0x03b5, 0x03b5, 0x03b5, 0x03b5, 0x03b5, 0x1f16, 0x1f17, 0x03b5, 0x03b5, 0x03b5, 0x03b5, 0x03b5, 0x03b5, 0x1f1e, 0x1f1f, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, }; static const unsigned short gNormalizeTable1f40[] = { /* U+1f40 */ 0x03bf, 0x03bf, 0x03bf, 0x03bf, 0x03bf, 0x03bf, 0x1f46, 0x1f47, 0x03bf, 0x03bf, 0x03bf, 0x03bf, 0x03bf, 0x03bf, 0x1f4e, 0x1f4f, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x1f58, 0x03c5, 0x1f5a, 0x03c5, 0x1f5c, 0x03c5, 0x1f5e, 0x03c5, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03b1, 0x03b1, 0x03b5, 0x03b5, 0x03b7, 0x03b7, 0x03b9, 0x03b9, 0x03bf, 0x03bf, 0x03c5, 0x03c5, 0x03c9, 0x03c9, 0x1f7e, 0x1f7f, }; static const unsigned short gNormalizeTable1f80[] = { /* U+1f80 */ 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03b7, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03c9, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x1fb5, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x03b1, 0x0020, 0x03b9, 0x0020, }; static const unsigned short gNormalizeTable1fc0[] = { /* U+1fc0 */ 0x0020, 0x0020, 0x03b7, 0x03b7, 0x03b7, 0x1fc5, 0x03b7, 0x03b7, 0x03b5, 0x03b5, 0x03b7, 0x03b7, 0x03b7, 0x0020, 0x0020, 0x0020, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x1fd4, 0x1fd5, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x03b9, 0x1fdc, 0x0020, 0x0020, 0x0020, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c1, 0x03c1, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c5, 0x03c1, 0x0020, 0x0020, 0x0060, 0x1ff0, 0x1ff1, 0x03c9, 0x03c9, 0x03c9, 0x1ff5, 0x03c9, 0x03c9, 0x03bf, 0x03bf, 0x03c9, 0x03c9, 0x03c9, 0x0020, 0x0020, 0x1fff, }; static const unsigned short gNormalizeTable2000[] = { /* U+2000 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x2010, 0x2010, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x0020, 0x2018, 0x2019, 0x201a, 0x201b, 0x201c, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2023, 0x002e, 0x002e, 0x002e, 0x2027, 0x2028, 0x2029, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x2030, 0x2031, 0x2032, 0x2032, 0x2032, 0x2035, 0x2035, 0x2035, 0x2038, 0x2039, 0x203a, 0x203b, 0x0021, 0x203d, 0x0020, 0x203f, }; static const unsigned short gNormalizeTable2040[] = { /* U+2040 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x003f, 0x003f, 0x0021, 0x204a, 0x204b, 0x204c, 0x204d, 0x204e, 0x204f, 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2032, 0x2058, 0x2059, 0x205a, 0x205b, 0x205c, 0x205d, 0x205e, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0030, 0x0069, 0x2072, 0x2073, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x002b, 0x2212, 0x003d, 0x0028, 0x0029, 0x006e, }; static const unsigned short gNormalizeTable2080[] = { /* U+2080 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x002b, 0x2212, 0x003d, 0x0028, 0x0029, 0x208f, 0x0061, 0x0065, 0x006f, 0x0078, 0x0259, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209a, 0x209b, 0x209c, 0x209d, 0x209e, 0x209f, 0x20a0, 0x20a1, 0x20a2, 0x20a3, 0x20a4, 0x20a5, 0x20a6, 0x20a7, 0x0072, 0x20a9, 0x20aa, 0x20ab, 0x20ac, 0x20ad, 0x20ae, 0x20af, 0x20b0, 0x20b1, 0x20b2, 0x20b3, 0x20b4, 0x20b5, 0x20b6, 0x20b7, 0x20b8, 0x20b9, 0x20ba, 0x20bb, 0x20bc, 0x20bd, 0x20be, 0x20bf, }; static const unsigned short gNormalizeTable2100[] = { /* U+2100 */ 0x0061, 0x0061, 0x0063, 0x00b0, 0x2104, 0x0063, 0x0063, 0x025b, 0x2108, 0x00b0, 0x0067, 0x0068, 0x0068, 0x0068, 0x0068, 0x0127, 0x0069, 0x0069, 0x006c, 0x006c, 0x2114, 0x006e, 0x006e, 0x2117, 0x2118, 0x0070, 0x0071, 0x0072, 0x0072, 0x0072, 0x211e, 0x211f, 0x0073, 0x0074, 0x0074, 0x2123, 0x007a, 0x2125, 0x03c9, 0x2127, 0x007a, 0x2129, 0x006b, 0x0061, 0x0062, 0x0063, 0x212e, 0x0065, 0x0065, 0x0066, 0x214e, 0x006d, 0x006f, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x0069, 0x213a, 0x0066, 0x03c0, 0x03b3, 0x03b3, 0x03c0, }; static const unsigned short gNormalizeTable2140[] = { /* U+2140 */ 0x2211, 0x2141, 0x2142, 0x2143, 0x2144, 0x0064, 0x0064, 0x0065, 0x0069, 0x006a, 0x214a, 0x214b, 0x214c, 0x214d, 0x214e, 0x214f, 0x0031, 0x0031, 0x0031, 0x0031, 0x0032, 0x0031, 0x0032, 0x0033, 0x0034, 0x0031, 0x0035, 0x0031, 0x0033, 0x0035, 0x0037, 0x0031, 0x0069, 0x0069, 0x0069, 0x0069, 0x0076, 0x0076, 0x0076, 0x0076, 0x0069, 0x0078, 0x0078, 0x0078, 0x006c, 0x0063, 0x0064, 0x006d, 0x0069, 0x0069, 0x0069, 0x0069, 0x0076, 0x0076, 0x0076, 0x0076, 0x0069, 0x0078, 0x0078, 0x0078, 0x006c, 0x0063, 0x0064, 0x006d, }; static const unsigned short gNormalizeTable2180[] = { /* U+2180 */ 0x2180, 0x2181, 0x2182, 0x2184, 0x2184, 0x2185, 0x2186, 0x2187, 0x2188, 0x0030, 0x218a, 0x218b, 0x218c, 0x218d, 0x218e, 0x218f, 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x2190, 0x2192, 0x219c, 0x219d, 0x219e, 0x219f, 0x21a0, 0x21a1, 0x21a2, 0x21a3, 0x21a4, 0x21a5, 0x21a6, 0x21a7, 0x21a8, 0x21a9, 0x21aa, 0x21ab, 0x21ac, 0x21ad, 0x2194, 0x21af, 0x21b0, 0x21b1, 0x21b2, 0x21b3, 0x21b4, 0x21b5, 0x21b6, 0x21b7, 0x21b8, 0x21b9, 0x21ba, 0x21bb, 0x21bc, 0x21bd, 0x21be, 0x21bf, }; static const unsigned short gNormalizeTable21c0[] = { /* U+21c0 */ 0x21c0, 0x21c1, 0x21c2, 0x21c3, 0x21c4, 0x21c5, 0x21c6, 0x21c7, 0x21c8, 0x21c9, 0x21ca, 0x21cb, 0x21cc, 0x21d0, 0x21d4, 0x21d2, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x21d5, 0x21d6, 0x21d7, 0x21d8, 0x21d9, 0x21da, 0x21db, 0x21dc, 0x21dd, 0x21de, 0x21df, 0x21e0, 0x21e1, 0x21e2, 0x21e3, 0x21e4, 0x21e5, 0x21e6, 0x21e7, 0x21e8, 0x21e9, 0x21ea, 0x21eb, 0x21ec, 0x21ed, 0x21ee, 0x21ef, 0x21f0, 0x21f1, 0x21f2, 0x21f3, 0x21f4, 0x21f5, 0x21f6, 0x21f7, 0x21f8, 0x21f9, 0x21fa, 0x21fb, 0x21fc, 0x21fd, 0x21fe, 0x21ff, }; static const unsigned short gNormalizeTable2200[] = { /* U+2200 */ 0x2200, 0x2201, 0x2202, 0x2203, 0x2203, 0x2205, 0x2206, 0x2207, 0x2208, 0x2208, 0x220a, 0x220b, 0x220b, 0x220d, 0x220e, 0x220f, 0x2210, 0x2211, 0x2212, 0x2213, 0x2214, 0x2215, 0x2216, 0x2217, 0x2218, 0x2219, 0x221a, 0x221b, 0x221c, 0x221d, 0x221e, 0x221f, 0x2220, 0x2221, 0x2222, 0x2223, 0x2223, 0x2225, 0x2225, 0x2227, 0x2228, 0x2229, 0x222a, 0x222b, 0x222b, 0x222b, 0x222e, 0x222e, 0x222e, 0x2231, 0x2232, 0x2233, 0x2234, 0x2235, 0x2236, 0x2237, 0x2238, 0x2239, 0x223a, 0x223b, 0x223c, 0x223d, 0x223e, 0x223f, }; static const unsigned short gNormalizeTable2240[] = { /* U+2240 */ 0x2240, 0x223c, 0x2242, 0x2243, 0x2243, 0x2245, 0x2246, 0x2245, 0x2248, 0x2248, 0x224a, 0x224b, 0x224c, 0x224d, 0x224e, 0x224f, 0x2250, 0x2251, 0x2252, 0x2253, 0x2254, 0x2255, 0x2256, 0x2257, 0x2258, 0x2259, 0x225a, 0x225b, 0x225c, 0x225d, 0x225e, 0x225f, 0x003d, 0x2261, 0x2261, 0x2263, 0x2264, 0x2265, 0x2266, 0x2267, 0x2268, 0x2269, 0x226a, 0x226b, 0x226c, 0x224d, 0x003c, 0x003e, 0x2264, 0x2265, 0x2272, 0x2273, 0x2272, 0x2273, 0x2276, 0x2277, 0x2276, 0x2277, 0x227a, 0x227b, 0x227c, 0x227d, 0x227e, 0x227f, }; static const unsigned short gNormalizeTable2280[] = { /* U+2280 */ 0x227a, 0x227b, 0x2282, 0x2283, 0x2282, 0x2283, 0x2286, 0x2287, 0x2286, 0x2287, 0x228a, 0x228b, 0x228c, 0x228d, 0x228e, 0x228f, 0x2290, 0x2291, 0x2292, 0x2293, 0x2294, 0x2295, 0x2296, 0x2297, 0x2298, 0x2299, 0x229a, 0x229b, 0x229c, 0x229d, 0x229e, 0x229f, 0x22a0, 0x22a1, 0x22a2, 0x22a3, 0x22a4, 0x22a5, 0x22a6, 0x22a7, 0x22a8, 0x22a9, 0x22aa, 0x22ab, 0x22a2, 0x22a8, 0x22a9, 0x22ab, 0x22b0, 0x22b1, 0x22b2, 0x22b3, 0x22b4, 0x22b5, 0x22b6, 0x22b7, 0x22b8, 0x22b9, 0x22ba, 0x22bb, 0x22bc, 0x22bd, 0x22be, 0x22bf, }; static const unsigned short gNormalizeTable22c0[] = { /* U+22c0 */ 0x22c0, 0x22c1, 0x22c2, 0x22c3, 0x22c4, 0x22c5, 0x22c6, 0x22c7, 0x22c8, 0x22c9, 0x22ca, 0x22cb, 0x22cc, 0x22cd, 0x22ce, 0x22cf, 0x22d0, 0x22d1, 0x22d2, 0x22d3, 0x22d4, 0x22d5, 0x22d6, 0x22d7, 0x22d8, 0x22d9, 0x22da, 0x22db, 0x22dc, 0x22dd, 0x22de, 0x22df, 0x227c, 0x227d, 0x2291, 0x2292, 0x22e4, 0x22e5, 0x22e6, 0x22e7, 0x22e8, 0x22e9, 0x22b2, 0x22b3, 0x22b4, 0x22b5, 0x22ee, 0x22ef, 0x22f0, 0x22f1, 0x22f2, 0x22f3, 0x22f4, 0x22f5, 0x22f6, 0x22f7, 0x22f8, 0x22f9, 0x22fa, 0x22fb, 0x22fc, 0x22fd, 0x22fe, 0x22ff, }; static const unsigned short gNormalizeTable2300[] = { /* U+2300 */ 0x2300, 0x2301, 0x2302, 0x2303, 0x2304, 0x2305, 0x2306, 0x2307, 0x2308, 0x2309, 0x230a, 0x230b, 0x230c, 0x230d, 0x230e, 0x230f, 0x2310, 0x2311, 0x2312, 0x2313, 0x2314, 0x2315, 0x2316, 0x2317, 0x2318, 0x2319, 0x231a, 0x231b, 0x231c, 0x231d, 0x231e, 0x231f, 0x2320, 0x2321, 0x2322, 0x2323, 0x2324, 0x2325, 0x2326, 0x2327, 0x2328, 0x3008, 0x3009, 0x232b, 0x232c, 0x232d, 0x232e, 0x232f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, 0x2337, 0x2338, 0x2339, 0x233a, 0x233b, 0x233c, 0x233d, 0x233e, 0x233f, }; static const unsigned short gNormalizeTable2440[] = { /* U+2440 */ 0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445, 0x2446, 0x2447, 0x2448, 0x2449, 0x244a, 0x244b, 0x244c, 0x244d, 0x244e, 0x244f, 0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456, 0x2457, 0x2458, 0x2459, 0x245a, 0x245b, 0x245c, 0x245d, 0x245e, 0x245f, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0032, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, }; static const unsigned short gNormalizeTable2480[] = { /* U+2480 */ 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0032, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, }; static const unsigned short gNormalizeTable24c0[] = { /* U+24c0 */ 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x0030, 0x24eb, 0x24ec, 0x24ed, 0x24ee, 0x24ef, 0x24f0, 0x24f1, 0x24f2, 0x24f3, 0x24f4, 0x24f5, 0x24f6, 0x24f7, 0x24f8, 0x24f9, 0x24fa, 0x24fb, 0x24fc, 0x24fd, 0x24fe, 0x24ff, }; static const unsigned short gNormalizeTable2a00[] = { /* U+2a00 */ 0x2a00, 0x2a01, 0x2a02, 0x2a03, 0x2a04, 0x2a05, 0x2a06, 0x2a07, 0x2a08, 0x2a09, 0x2a0a, 0x2a0b, 0x222b, 0x2a0d, 0x2a0e, 0x2a0f, 0x2a10, 0x2a11, 0x2a12, 0x2a13, 0x2a14, 0x2a15, 0x2a16, 0x2a17, 0x2a18, 0x2a19, 0x2a1a, 0x2a1b, 0x2a1c, 0x2a1d, 0x2a1e, 0x2a1f, 0x2a20, 0x2a21, 0x2a22, 0x2a23, 0x2a24, 0x2a25, 0x2a26, 0x2a27, 0x2a28, 0x2a29, 0x2a2a, 0x2a2b, 0x2a2c, 0x2a2d, 0x2a2e, 0x2a2f, 0x2a30, 0x2a31, 0x2a32, 0x2a33, 0x2a34, 0x2a35, 0x2a36, 0x2a37, 0x2a38, 0x2a39, 0x2a3a, 0x2a3b, 0x2a3c, 0x2a3d, 0x2a3e, 0x2a3f, }; static const unsigned short gNormalizeTable2a40[] = { /* U+2a40 */ 0x2a40, 0x2a41, 0x2a42, 0x2a43, 0x2a44, 0x2a45, 0x2a46, 0x2a47, 0x2a48, 0x2a49, 0x2a4a, 0x2a4b, 0x2a4c, 0x2a4d, 0x2a4e, 0x2a4f, 0x2a50, 0x2a51, 0x2a52, 0x2a53, 0x2a54, 0x2a55, 0x2a56, 0x2a57, 0x2a58, 0x2a59, 0x2a5a, 0x2a5b, 0x2a5c, 0x2a5d, 0x2a5e, 0x2a5f, 0x2a60, 0x2a61, 0x2a62, 0x2a63, 0x2a64, 0x2a65, 0x2a66, 0x2a67, 0x2a68, 0x2a69, 0x2a6a, 0x2a6b, 0x2a6c, 0x2a6d, 0x2a6e, 0x2a6f, 0x2a70, 0x2a71, 0x2a72, 0x2a73, 0x003a, 0x003d, 0x003d, 0x2a77, 0x2a78, 0x2a79, 0x2a7a, 0x2a7b, 0x2a7c, 0x2a7d, 0x2a7e, 0x2a7f, }; static const unsigned short gNormalizeTable2ac0[] = { /* U+2ac0 */ 0x2ac0, 0x2ac1, 0x2ac2, 0x2ac3, 0x2ac4, 0x2ac5, 0x2ac6, 0x2ac7, 0x2ac8, 0x2ac9, 0x2aca, 0x2acb, 0x2acc, 0x2acd, 0x2ace, 0x2acf, 0x2ad0, 0x2ad1, 0x2ad2, 0x2ad3, 0x2ad4, 0x2ad5, 0x2ad6, 0x2ad7, 0x2ad8, 0x2ad9, 0x2ada, 0x2adb, 0x2add, 0x2add, 0x2ade, 0x2adf, 0x2ae0, 0x2ae1, 0x2ae2, 0x2ae3, 0x2ae4, 0x2ae5, 0x2ae6, 0x2ae7, 0x2ae8, 0x2ae9, 0x2aea, 0x2aeb, 0x2aec, 0x2aed, 0x2aee, 0x2aef, 0x2af0, 0x2af1, 0x2af2, 0x2af3, 0x2af4, 0x2af5, 0x2af6, 0x2af7, 0x2af8, 0x2af9, 0x2afa, 0x2afb, 0x2afc, 0x2afd, 0x2afe, 0x2aff, }; static const unsigned short gNormalizeTable2c00[] = { /* U+2c00 */ 0x2c30, 0x2c31, 0x2c32, 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37, 0x2c38, 0x2c39, 0x2c3a, 0x2c3b, 0x2c3c, 0x2c3d, 0x2c3e, 0x2c3f, 0x2c40, 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, 0x2c53, 0x2c54, 0x2c55, 0x2c56, 0x2c57, 0x2c58, 0x2c59, 0x2c5a, 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c2f, 0x2c30, 0x2c31, 0x2c32, 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37, 0x2c38, 0x2c39, 0x2c3a, 0x2c3b, 0x2c3c, 0x2c3d, 0x2c3e, 0x2c3f, }; static const unsigned short gNormalizeTable2c40[] = { /* U+2c40 */ 0x2c40, 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, 0x2c53, 0x2c54, 0x2c55, 0x2c56, 0x2c57, 0x2c58, 0x2c59, 0x2c5a, 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c5f, 0x2c61, 0x2c61, 0x026b, 0x1d7d, 0x027d, 0x2c65, 0x2c66, 0x2c68, 0x2c68, 0x2c6a, 0x2c6a, 0x2c6c, 0x2c6c, 0x0251, 0x0271, 0x0250, 0x0252, 0x2c71, 0x2c73, 0x2c73, 0x2c74, 0x2c76, 0x2c76, 0x2c77, 0x2c78, 0x2c79, 0x2c7a, 0x2c7b, 0x006a, 0x0076, 0x023f, 0x0240, }; static const unsigned short gNormalizeTable2c80[] = { /* U+2c80 */ 0x2c81, 0x2c81, 0x2c83, 0x2c83, 0x2c85, 0x2c85, 0x2c87, 0x2c87, 0x2c89, 0x2c89, 0x2c8b, 0x2c8b, 0x2c8d, 0x2c8d, 0x2c8f, 0x2c8f, 0x2c91, 0x2c91, 0x2c93, 0x2c93, 0x2c95, 0x2c95, 0x2c97, 0x2c97, 0x2c99, 0x2c99, 0x2c9b, 0x2c9b, 0x2c9d, 0x2c9d, 0x2c9f, 0x2c9f, 0x2ca1, 0x2ca1, 0x2ca3, 0x2ca3, 0x2ca5, 0x2ca5, 0x2ca7, 0x2ca7, 0x2ca9, 0x2ca9, 0x2cab, 0x2cab, 0x2cad, 0x2cad, 0x2caf, 0x2caf, 0x2cb1, 0x2cb1, 0x2cb3, 0x2cb3, 0x2cb5, 0x2cb5, 0x2cb7, 0x2cb7, 0x2cb9, 0x2cb9, 0x2cbb, 0x2cbb, 0x2cbd, 0x2cbd, 0x2cbf, 0x2cbf, }; static const unsigned short gNormalizeTable2cc0[] = { /* U+2cc0 */ 0x2cc1, 0x2cc1, 0x2cc3, 0x2cc3, 0x2cc5, 0x2cc5, 0x2cc7, 0x2cc7, 0x2cc9, 0x2cc9, 0x2ccb, 0x2ccb, 0x2ccd, 0x2ccd, 0x2ccf, 0x2ccf, 0x2cd1, 0x2cd1, 0x2cd3, 0x2cd3, 0x2cd5, 0x2cd5, 0x2cd7, 0x2cd7, 0x2cd9, 0x2cd9, 0x2cdb, 0x2cdb, 0x2cdd, 0x2cdd, 0x2cdf, 0x2cdf, 0x2ce1, 0x2ce1, 0x2ce3, 0x2ce3, 0x2ce4, 0x2ce5, 0x2ce6, 0x2ce7, 0x2ce8, 0x2ce9, 0x2cea, 0x2cec, 0x2cec, 0x2cee, 0x2cee, 0x2cef, 0x2cf0, 0x2cf1, 0x2cf2, 0x2cf3, 0x2cf4, 0x2cf5, 0x2cf6, 0x2cf7, 0x2cf8, 0x2cf9, 0x2cfa, 0x2cfb, 0x2cfc, 0x2cfd, 0x2cfe, 0x2cff, }; static const unsigned short gNormalizeTable2d40[] = { /* U+2d40 */ 0x2d40, 0x2d41, 0x2d42, 0x2d43, 0x2d44, 0x2d45, 0x2d46, 0x2d47, 0x2d48, 0x2d49, 0x2d4a, 0x2d4b, 0x2d4c, 0x2d4d, 0x2d4e, 0x2d4f, 0x2d50, 0x2d51, 0x2d52, 0x2d53, 0x2d54, 0x2d55, 0x2d56, 0x2d57, 0x2d58, 0x2d59, 0x2d5a, 0x2d5b, 0x2d5c, 0x2d5d, 0x2d5e, 0x2d5f, 0x2d60, 0x2d61, 0x2d62, 0x2d63, 0x2d64, 0x2d65, 0x2d66, 0x2d67, 0x2d68, 0x2d69, 0x2d6a, 0x2d6b, 0x2d6c, 0x2d6d, 0x2d6e, 0x2d61, 0x2d70, 0x2d71, 0x2d72, 0x2d73, 0x2d74, 0x2d75, 0x2d76, 0x2d77, 0x2d78, 0x2d79, 0x2d7a, 0x2d7b, 0x2d7c, 0x2d7d, 0x2d7e, 0x2d7f, }; static const unsigned short gNormalizeTable2e80[] = { /* U+2e80 */ 0x2e80, 0x2e81, 0x2e82, 0x2e83, 0x2e84, 0x2e85, 0x2e86, 0x2e87, 0x2e88, 0x2e89, 0x2e8a, 0x2e8b, 0x2e8c, 0x2e8d, 0x2e8e, 0x2e8f, 0x2e90, 0x2e91, 0x2e92, 0x2e93, 0x2e94, 0x2e95, 0x2e96, 0x2e97, 0x2e98, 0x2e99, 0x2e9a, 0x2e9b, 0x2e9c, 0x2e9d, 0x2e9e, 0x6bcd, 0x2ea0, 0x2ea1, 0x2ea2, 0x2ea3, 0x2ea4, 0x2ea5, 0x2ea6, 0x2ea7, 0x2ea8, 0x2ea9, 0x2eaa, 0x2eab, 0x2eac, 0x2ead, 0x2eae, 0x2eaf, 0x2eb0, 0x2eb1, 0x2eb2, 0x2eb3, 0x2eb4, 0x2eb5, 0x2eb6, 0x2eb7, 0x2eb8, 0x2eb9, 0x2eba, 0x2ebb, 0x2ebc, 0x2ebd, 0x2ebe, 0x2ebf, }; static const unsigned short gNormalizeTable2ec0[] = { /* U+2ec0 */ 0x2ec0, 0x2ec1, 0x2ec2, 0x2ec3, 0x2ec4, 0x2ec5, 0x2ec6, 0x2ec7, 0x2ec8, 0x2ec9, 0x2eca, 0x2ecb, 0x2ecc, 0x2ecd, 0x2ece, 0x2ecf, 0x2ed0, 0x2ed1, 0x2ed2, 0x2ed3, 0x2ed4, 0x2ed5, 0x2ed6, 0x2ed7, 0x2ed8, 0x2ed9, 0x2eda, 0x2edb, 0x2edc, 0x2edd, 0x2ede, 0x2edf, 0x2ee0, 0x2ee1, 0x2ee2, 0x2ee3, 0x2ee4, 0x2ee5, 0x2ee6, 0x2ee7, 0x2ee8, 0x2ee9, 0x2eea, 0x2eeb, 0x2eec, 0x2eed, 0x2eee, 0x2eef, 0x2ef0, 0x2ef1, 0x2ef2, 0x9f9f, 0x2ef4, 0x2ef5, 0x2ef6, 0x2ef7, 0x2ef8, 0x2ef9, 0x2efa, 0x2efb, 0x2efc, 0x2efd, 0x2efe, 0x2eff, }; static const unsigned short gNormalizeTable2f00[] = { /* U+2f00 */ 0x4e00, 0x4e28, 0x4e36, 0x4e3f, 0x4e59, 0x4e85, 0x4e8c, 0x4ea0, 0x4eba, 0x513f, 0x5165, 0x516b, 0x5182, 0x5196, 0x51ab, 0x51e0, 0x51f5, 0x5200, 0x529b, 0x52f9, 0x5315, 0x531a, 0x5338, 0x5341, 0x535c, 0x5369, 0x5382, 0x53b6, 0x53c8, 0x53e3, 0x56d7, 0x571f, 0x58eb, 0x5902, 0x590a, 0x5915, 0x5927, 0x5973, 0x5b50, 0x5b80, 0x5bf8, 0x5c0f, 0x5c22, 0x5c38, 0x5c6e, 0x5c71, 0x5ddb, 0x5de5, 0x5df1, 0x5dfe, 0x5e72, 0x5e7a, 0x5e7f, 0x5ef4, 0x5efe, 0x5f0b, 0x5f13, 0x5f50, 0x5f61, 0x5f73, 0x5fc3, 0x6208, 0x6236, 0x624b, }; static const unsigned short gNormalizeTable2f40[] = { /* U+2f40 */ 0x652f, 0x6534, 0x6587, 0x6597, 0x65a4, 0x65b9, 0x65e0, 0x65e5, 0x66f0, 0x6708, 0x6728, 0x6b20, 0x6b62, 0x6b79, 0x6bb3, 0x6bcb, 0x6bd4, 0x6bdb, 0x6c0f, 0x6c14, 0x6c34, 0x706b, 0x722a, 0x7236, 0x723b, 0x723f, 0x7247, 0x7259, 0x725b, 0x72ac, 0x7384, 0x7389, 0x74dc, 0x74e6, 0x7518, 0x751f, 0x7528, 0x7530, 0x758b, 0x7592, 0x7676, 0x767d, 0x76ae, 0x76bf, 0x76ee, 0x77db, 0x77e2, 0x77f3, 0x793a, 0x79b8, 0x79be, 0x7a74, 0x7acb, 0x7af9, 0x7c73, 0x7cf8, 0x7f36, 0x7f51, 0x7f8a, 0x7fbd, 0x8001, 0x800c, 0x8012, 0x8033, }; static const unsigned short gNormalizeTable2f80[] = { /* U+2f80 */ 0x807f, 0x8089, 0x81e3, 0x81ea, 0x81f3, 0x81fc, 0x820c, 0x821b, 0x821f, 0x826e, 0x8272, 0x8278, 0x864d, 0x866b, 0x8840, 0x884c, 0x8863, 0x897e, 0x898b, 0x89d2, 0x8a00, 0x8c37, 0x8c46, 0x8c55, 0x8c78, 0x8c9d, 0x8d64, 0x8d70, 0x8db3, 0x8eab, 0x8eca, 0x8f9b, 0x8fb0, 0x8fb5, 0x9091, 0x9149, 0x91c6, 0x91cc, 0x91d1, 0x9577, 0x9580, 0x961c, 0x96b6, 0x96b9, 0x96e8, 0x9751, 0x975e, 0x9762, 0x9769, 0x97cb, 0x97ed, 0x97f3, 0x9801, 0x98a8, 0x98db, 0x98df, 0x9996, 0x9999, 0x99ac, 0x9aa8, 0x9ad8, 0x9adf, 0x9b25, 0x9b2f, }; static const unsigned short gNormalizeTable2fc0[] = { /* U+2fc0 */ 0x9b32, 0x9b3c, 0x9b5a, 0x9ce5, 0x9e75, 0x9e7f, 0x9ea5, 0x9ebb, 0x9ec3, 0x9ecd, 0x9ed1, 0x9ef9, 0x9efd, 0x9f0e, 0x9f13, 0x9f20, 0x9f3b, 0x9f4a, 0x9f52, 0x9f8d, 0x9f9c, 0x9fa0, 0x2fd6, 0x2fd7, 0x2fd8, 0x2fd9, 0x2fda, 0x2fdb, 0x2fdc, 0x2fdd, 0x2fde, 0x2fdf, 0x2fe0, 0x2fe1, 0x2fe2, 0x2fe3, 0x2fe4, 0x2fe5, 0x2fe6, 0x2fe7, 0x2fe8, 0x2fe9, 0x2fea, 0x2feb, 0x2fec, 0x2fed, 0x2fee, 0x2fef, 0x2ff0, 0x2ff1, 0x2ff2, 0x2ff3, 0x2ff4, 0x2ff5, 0x2ff6, 0x2ff7, 0x2ff8, 0x2ff9, 0x2ffa, 0x2ffb, 0x2ffc, 0x2ffd, 0x2ffe, 0x2fff, }; static const unsigned short gNormalizeTable3000[] = { /* U+3000 */ 0x0020, 0x3001, 0x3002, 0x3003, 0x3004, 0x3005, 0x3006, 0x3007, 0x3008, 0x3009, 0x300a, 0x300b, 0x300c, 0x300d, 0x300e, 0x300f, 0x3010, 0x3011, 0x3012, 0x3013, 0x3014, 0x3015, 0x3016, 0x3017, 0x3018, 0x3019, 0x301a, 0x301b, 0x301c, 0x301d, 0x301e, 0x301f, 0x3020, 0x3021, 0x3022, 0x3023, 0x3024, 0x3025, 0x3026, 0x3027, 0x3028, 0x3029, 0x302a, 0x302b, 0x302c, 0x302d, 0x302e, 0x302f, 0x3030, 0x3031, 0x3032, 0x3033, 0x3034, 0x3035, 0x3012, 0x3037, 0x5341, 0x5344, 0x5345, 0x303b, 0x303c, 0x303d, 0x303e, 0x303f, }; static const unsigned short gNormalizeTable3040[] = { /* U+3040 */ 0x3040, 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304a, 0x304b, 0x304b, 0x304d, 0x304d, 0x304f, 0x304f, 0x3051, 0x3051, 0x3053, 0x3053, 0x3055, 0x3055, 0x3057, 0x3057, 0x3059, 0x3059, 0x305b, 0x305b, 0x305d, 0x305d, 0x305f, 0x305f, 0x3061, 0x3061, 0x3063, 0x3064, 0x3064, 0x3066, 0x3066, 0x3068, 0x3068, 0x306a, 0x306b, 0x306c, 0x306d, 0x306e, 0x306f, 0x306f, 0x306f, 0x3072, 0x3072, 0x3072, 0x3075, 0x3075, 0x3075, 0x3078, 0x3078, 0x3078, 0x307b, 0x307b, 0x307b, 0x307e, 0x307f, }; static const unsigned short gNormalizeTable3080[] = { /* U+3080 */ 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, 0x3089, 0x308a, 0x308b, 0x308c, 0x308d, 0x308e, 0x308f, 0x3090, 0x3091, 0x3092, 0x3093, 0x3046, 0x3095, 0x3096, 0x3097, 0x3098, 0x3099, 0x309a, 0x0020, 0x0020, 0x309d, 0x309d, 0x3088, 0x30a0, 0x30a1, 0x30a2, 0x30a3, 0x30a4, 0x30a5, 0x30a6, 0x30a7, 0x30a8, 0x30a9, 0x30aa, 0x30ab, 0x30ab, 0x30ad, 0x30ad, 0x30af, 0x30af, 0x30b1, 0x30b1, 0x30b3, 0x30b3, 0x30b5, 0x30b5, 0x30b7, 0x30b7, 0x30b9, 0x30b9, 0x30bb, 0x30bb, 0x30bd, 0x30bd, 0x30bf, }; static const unsigned short gNormalizeTable30c0[] = { /* U+30c0 */ 0x30bf, 0x30c1, 0x30c1, 0x30c3, 0x30c4, 0x30c4, 0x30c6, 0x30c6, 0x30c8, 0x30c8, 0x30ca, 0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf, 0x30cf, 0x30cf, 0x30d2, 0x30d2, 0x30d2, 0x30d5, 0x30d5, 0x30d5, 0x30d8, 0x30d8, 0x30d8, 0x30db, 0x30db, 0x30db, 0x30de, 0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e3, 0x30e4, 0x30e5, 0x30e6, 0x30e7, 0x30e8, 0x30e9, 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ee, 0x30ef, 0x30f0, 0x30f1, 0x30f2, 0x30f3, 0x30a6, 0x30f5, 0x30f6, 0x30ef, 0x30f0, 0x30f1, 0x30f2, 0x30fb, 0x30fc, 0x30fd, 0x30fd, 0x30b3, }; static const unsigned short gNormalizeTable3100[] = { /* U+3100 */ 0x3100, 0x3101, 0x3102, 0x3103, 0x3104, 0x3105, 0x3106, 0x3107, 0x3108, 0x3109, 0x310a, 0x310b, 0x310c, 0x310d, 0x310e, 0x310f, 0x3110, 0x3111, 0x3112, 0x3113, 0x3114, 0x3115, 0x3116, 0x3117, 0x3118, 0x3119, 0x311a, 0x311b, 0x311c, 0x311d, 0x311e, 0x311f, 0x3120, 0x3121, 0x3122, 0x3123, 0x3124, 0x3125, 0x3126, 0x3127, 0x3128, 0x3129, 0x312a, 0x312b, 0x312c, 0x312d, 0x312e, 0x312f, 0x3130, 0x1100, 0x1101, 0x11aa, 0x1102, 0x11ac, 0x11ad, 0x1103, 0x1104, 0x1105, 0x11b0, 0x11b1, 0x11b2, 0x11b3, 0x11b4, 0x11b5, }; static const unsigned short gNormalizeTable3140[] = { /* U+3140 */ 0x111a, 0x1106, 0x1107, 0x1108, 0x1121, 0x1109, 0x110a, 0x110b, 0x110c, 0x110d, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112, 0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, 0x1168, 0x1169, 0x116a, 0x116b, 0x116c, 0x116d, 0x116e, 0x116f, 0x1170, 0x1171, 0x1172, 0x1173, 0x1174, 0x1175, 0x0020, 0x1114, 0x1115, 0x11c7, 0x11c8, 0x11cc, 0x11ce, 0x11d3, 0x11d7, 0x11d9, 0x111c, 0x11dd, 0x11df, 0x111d, 0x111e, 0x1120, 0x1122, 0x1123, 0x1127, 0x1129, 0x112b, 0x112c, 0x112d, 0x112e, 0x112f, 0x1132, 0x1136, 0x1140, }; static const unsigned short gNormalizeTable3180[] = { /* U+3180 */ 0x1147, 0x114c, 0x11f1, 0x11f2, 0x1157, 0x1158, 0x1159, 0x1184, 0x1185, 0x1188, 0x1191, 0x1192, 0x1194, 0x119e, 0x11a1, 0x318f, 0x3190, 0x3191, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e0a, 0x4e2d, 0x4e0b, 0x7532, 0x4e59, 0x4e19, 0x4e01, 0x5929, 0x5730, 0x4eba, 0x31a0, 0x31a1, 0x31a2, 0x31a3, 0x31a4, 0x31a5, 0x31a6, 0x31a7, 0x31a8, 0x31a9, 0x31aa, 0x31ab, 0x31ac, 0x31ad, 0x31ae, 0x31af, 0x31b0, 0x31b1, 0x31b2, 0x31b3, 0x31b4, 0x31b5, 0x31b6, 0x31b7, 0x31b8, 0x31b9, 0x31ba, 0x31bb, 0x31bc, 0x31bd, 0x31be, 0x31bf, }; static const unsigned short gNormalizeTable3200[] = { /* U+3200 */ 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x321f, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, }; static const unsigned short gNormalizeTable3240[] = { /* U+3240 */ 0x0028, 0x0028, 0x0028, 0x0028, 0x554f, 0x5e7c, 0x6587, 0x7b8f, 0x3248, 0x3249, 0x324a, 0x324b, 0x324c, 0x324d, 0x324e, 0x324f, 0x0070, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0033, 0x0033, 0x0033, 0x0033, 0x0033, 0x0033, 0x1100, 0x1102, 0x1103, 0x1105, 0x1106, 0x1107, 0x1109, 0x110b, 0x110c, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112, 0x1100, 0x1102, 0x1103, 0x1105, 0x1106, 0x1107, 0x1109, 0x110b, 0x110c, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112, 0x110e, 0x110c, 0x110b, 0x327f, }; static const unsigned short gNormalizeTable3280[] = { /* U+3280 */ 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d, 0x5341, 0x6708, 0x706b, 0x6c34, 0x6728, 0x91d1, 0x571f, 0x65e5, 0x682a, 0x6709, 0x793e, 0x540d, 0x7279, 0x8ca1, 0x795d, 0x52b4, 0x79d8, 0x7537, 0x5973, 0x9069, 0x512a, 0x5370, 0x6ce8, 0x9805, 0x4f11, 0x5199, 0x6b63, 0x4e0a, 0x4e2d, 0x4e0b, 0x5de6, 0x53f3, 0x533b, 0x5b97, 0x5b66, 0x76e3, 0x4f01, 0x8cc7, 0x5354, 0x591c, 0x0033, 0x0033, 0x0033, 0x0033, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0035, }; static const unsigned short gNormalizeTable32c0[] = { /* U+32c0 */ 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0031, 0x0031, 0x0031, 0x0068, 0x0065, 0x0065, 0x006c, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad, 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb, 0x30bd, 0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, 0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de, 0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9, 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f0, 0x30f1, 0x30f2, 0x32ff, }; static const unsigned short gNormalizeTable3300[] = { /* U+3300 */ 0x30a2, 0x30a2, 0x30a2, 0x30a2, 0x30a4, 0x30a4, 0x30a6, 0x30a8, 0x30a8, 0x30aa, 0x30aa, 0x30ab, 0x30ab, 0x30ab, 0x30ab, 0x30ab, 0x30ad, 0x30ad, 0x30ad, 0x30ad, 0x30ad, 0x30ad, 0x30ad, 0x30ad, 0x30af, 0x30af, 0x30af, 0x30af, 0x30b1, 0x30b3, 0x30b3, 0x30b5, 0x30b5, 0x30b7, 0x30bb, 0x30bb, 0x30bf, 0x30c6, 0x30c8, 0x30c8, 0x30ca, 0x30ce, 0x30cf, 0x30cf, 0x30cf, 0x30cf, 0x30d2, 0x30d2, 0x30d2, 0x30d2, 0x30d5, 0x30d5, 0x30d5, 0x30d5, 0x30d8, 0x30d8, 0x30d8, 0x30d8, 0x30d8, 0x30d8, 0x30d8, 0x30db, 0x30db, 0x30db, }; static const unsigned short gNormalizeTable3340[] = { /* U+3340 */ 0x30db, 0x30db, 0x30db, 0x30de, 0x30de, 0x30de, 0x30de, 0x30de, 0x30df, 0x30df, 0x30df, 0x30e1, 0x30e1, 0x30e1, 0x30e4, 0x30e4, 0x30e6, 0x30ea, 0x30ea, 0x30eb, 0x30eb, 0x30ec, 0x30ec, 0x30ef, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0068, 0x0064, 0x0061, 0x0062, 0x006f, 0x0070, 0x0064, 0x0064, 0x0064, 0x0069, 0x5e73, 0x662d, 0x5927, 0x660e, 0x682a, }; static const unsigned short gNormalizeTable3380[] = { /* U+3380 */ 0x0070, 0x006e, 0x03bc, 0x006d, 0x006b, 0x006b, 0x006d, 0x0067, 0x0063, 0x006b, 0x0070, 0x006e, 0x03bc, 0x03bc, 0x006d, 0x006b, 0x0068, 0x006b, 0x006d, 0x0067, 0x0074, 0x03bc, 0x006d, 0x0064, 0x006b, 0x0066, 0x006e, 0x03bc, 0x006d, 0x0063, 0x006b, 0x006d, 0x0063, 0x006d, 0x006b, 0x006d, 0x0063, 0x006d, 0x006b, 0x006d, 0x006d, 0x0070, 0x006b, 0x006d, 0x0067, 0x0072, 0x0072, 0x0072, 0x0070, 0x006e, 0x03bc, 0x006d, 0x0070, 0x006e, 0x03bc, 0x006d, 0x006b, 0x006d, 0x0070, 0x006e, 0x03bc, 0x006d, 0x006b, 0x006d, }; static const unsigned short gNormalizeTable33c0[] = { /* U+33c0 */ 0x006b, 0x006d, 0x0061, 0x0062, 0x0063, 0x0063, 0x0063, 0x0063, 0x0064, 0x0067, 0x0068, 0x0068, 0x0069, 0x006b, 0x006b, 0x006b, 0x006c, 0x006c, 0x006c, 0x006c, 0x006d, 0x006d, 0x006d, 0x0070, 0x0070, 0x0070, 0x0070, 0x0073, 0x0073, 0x0077, 0x0076, 0x0061, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0033, 0x0033, 0x0067, }; static const unsigned short gNormalizeTablea640[] = { /* U+a640 */ 0xa641, 0xa641, 0xa643, 0xa643, 0xa645, 0xa645, 0xa647, 0xa647, 0xa649, 0xa649, 0xa64b, 0xa64b, 0xa64d, 0xa64d, 0xa64f, 0xa64f, 0xa651, 0xa651, 0xa653, 0xa653, 0xa655, 0xa655, 0xa657, 0xa657, 0xa659, 0xa659, 0xa65b, 0xa65b, 0xa65d, 0xa65d, 0xa65f, 0xa65f, 0xa660, 0xa661, 0xa663, 0xa663, 0xa665, 0xa665, 0xa667, 0xa667, 0xa669, 0xa669, 0xa66b, 0xa66b, 0xa66d, 0xa66d, 0xa66e, 0xa66f, 0xa670, 0xa671, 0xa672, 0xa673, 0xa674, 0xa675, 0xa676, 0xa677, 0xa678, 0xa679, 0xa67a, 0xa67b, 0xa67c, 0xa67d, 0xa67e, 0xa67f, }; static const unsigned short gNormalizeTablea680[] = { /* U+a680 */ 0xa681, 0xa681, 0xa683, 0xa683, 0xa685, 0xa685, 0xa687, 0xa687, 0xa689, 0xa689, 0xa68b, 0xa68b, 0xa68d, 0xa68d, 0xa68f, 0xa68f, 0xa691, 0xa691, 0xa693, 0xa693, 0xa695, 0xa695, 0xa697, 0xa697, 0xa698, 0xa699, 0xa69a, 0xa69b, 0xa69c, 0xa69d, 0xa69e, 0xa69f, 0xa6a0, 0xa6a1, 0xa6a2, 0xa6a3, 0xa6a4, 0xa6a5, 0xa6a6, 0xa6a7, 0xa6a8, 0xa6a9, 0xa6aa, 0xa6ab, 0xa6ac, 0xa6ad, 0xa6ae, 0xa6af, 0xa6b0, 0xa6b1, 0xa6b2, 0xa6b3, 0xa6b4, 0xa6b5, 0xa6b6, 0xa6b7, 0xa6b8, 0xa6b9, 0xa6ba, 0xa6bb, 0xa6bc, 0xa6bd, 0xa6be, 0xa6bf, }; static const unsigned short gNormalizeTablea700[] = { /* U+a700 */ 0xa700, 0xa701, 0xa702, 0xa703, 0xa704, 0xa705, 0xa706, 0xa707, 0xa708, 0xa709, 0xa70a, 0xa70b, 0xa70c, 0xa70d, 0xa70e, 0xa70f, 0xa710, 0xa711, 0xa712, 0xa713, 0xa714, 0xa715, 0xa716, 0xa717, 0xa718, 0xa719, 0xa71a, 0xa71b, 0xa71c, 0xa71d, 0xa71e, 0xa71f, 0xa720, 0xa721, 0xa723, 0xa723, 0xa725, 0xa725, 0xa727, 0xa727, 0xa729, 0xa729, 0xa72b, 0xa72b, 0xa72d, 0xa72d, 0xa72f, 0xa72f, 0xa730, 0xa731, 0xa733, 0xa733, 0xa735, 0xa735, 0xa737, 0xa737, 0xa739, 0xa739, 0xa73b, 0xa73b, 0xa73d, 0xa73d, 0xa73f, 0xa73f, }; static const unsigned short gNormalizeTablea740[] = { /* U+a740 */ 0xa741, 0xa741, 0xa743, 0xa743, 0xa745, 0xa745, 0xa747, 0xa747, 0xa749, 0xa749, 0xa74b, 0xa74b, 0xa74d, 0xa74d, 0xa74f, 0xa74f, 0xa751, 0xa751, 0xa753, 0xa753, 0xa755, 0xa755, 0xa757, 0xa757, 0xa759, 0xa759, 0xa75b, 0xa75b, 0xa75d, 0xa75d, 0xa75f, 0xa75f, 0xa761, 0xa761, 0xa763, 0xa763, 0xa765, 0xa765, 0xa767, 0xa767, 0xa769, 0xa769, 0xa76b, 0xa76b, 0xa76d, 0xa76d, 0xa76f, 0xa76f, 0xa76f, 0xa771, 0xa772, 0xa773, 0xa774, 0xa775, 0xa776, 0xa777, 0xa778, 0xa77a, 0xa77a, 0xa77c, 0xa77c, 0x1d79, 0xa77f, 0xa77f, }; static const unsigned short gNormalizeTablea780[] = { /* U+a780 */ 0xa781, 0xa781, 0xa783, 0xa783, 0xa785, 0xa785, 0xa787, 0xa787, 0xa788, 0xa789, 0xa78a, 0xa78c, 0xa78c, 0xa78d, 0xa78e, 0xa78f, 0xa790, 0xa791, 0xa792, 0xa793, 0xa794, 0xa795, 0xa796, 0xa797, 0xa798, 0xa799, 0xa79a, 0xa79b, 0xa79c, 0xa79d, 0xa79e, 0xa79f, 0xa7a0, 0xa7a1, 0xa7a2, 0xa7a3, 0xa7a4, 0xa7a5, 0xa7a6, 0xa7a7, 0xa7a8, 0xa7a9, 0xa7aa, 0xa7ab, 0xa7ac, 0xa7ad, 0xa7ae, 0xa7af, 0xa7b0, 0xa7b1, 0xa7b2, 0xa7b3, 0xa7b4, 0xa7b5, 0xa7b6, 0xa7b7, 0xa7b8, 0xa7b9, 0xa7ba, 0xa7bb, 0xa7bc, 0xa7bd, 0xa7be, 0xa7bf, }; static const unsigned short gNormalizeTablef900[] = { /* U+f900 */ 0x8c48, 0x66f4, 0x8eca, 0x8cc8, 0x6ed1, 0x4e32, 0x53e5, 0x9f9c, 0x9f9c, 0x5951, 0x91d1, 0x5587, 0x5948, 0x61f6, 0x7669, 0x7f85, 0x863f, 0x87ba, 0x88f8, 0x908f, 0x6a02, 0x6d1b, 0x70d9, 0x73de, 0x843d, 0x916a, 0x99f1, 0x4e82, 0x5375, 0x6b04, 0x721b, 0x862d, 0x9e1e, 0x5d50, 0x6feb, 0x85cd, 0x8964, 0x62c9, 0x81d8, 0x881f, 0x5eca, 0x6717, 0x6d6a, 0x72fc, 0x90ce, 0x4f86, 0x51b7, 0x52de, 0x64c4, 0x6ad3, 0x7210, 0x76e7, 0x8001, 0x8606, 0x865c, 0x8def, 0x9732, 0x9b6f, 0x9dfa, 0x788c, 0x797f, 0x7da0, 0x83c9, 0x9304, }; static const unsigned short gNormalizeTablef940[] = { /* U+f940 */ 0x9e7f, 0x8ad6, 0x58df, 0x5f04, 0x7c60, 0x807e, 0x7262, 0x78ca, 0x8cc2, 0x96f7, 0x58d8, 0x5c62, 0x6a13, 0x6dda, 0x6f0f, 0x7d2f, 0x7e37, 0x964b, 0x52d2, 0x808b, 0x51dc, 0x51cc, 0x7a1c, 0x7dbe, 0x83f1, 0x9675, 0x8b80, 0x62cf, 0x6a02, 0x8afe, 0x4e39, 0x5be7, 0x6012, 0x7387, 0x7570, 0x5317, 0x78fb, 0x4fbf, 0x5fa9, 0x4e0d, 0x6ccc, 0x6578, 0x7d22, 0x53c3, 0x585e, 0x7701, 0x8449, 0x8aaa, 0x6bba, 0x8fb0, 0x6c88, 0x62fe, 0x82e5, 0x63a0, 0x7565, 0x4eae, 0x5169, 0x51c9, 0x6881, 0x7ce7, 0x826f, 0x8ad2, 0x91cf, 0x52f5, }; static const unsigned short gNormalizeTablef980[] = { /* U+f980 */ 0x5442, 0x5973, 0x5eec, 0x65c5, 0x6ffe, 0x792a, 0x95ad, 0x9a6a, 0x9e97, 0x9ece, 0x529b, 0x66c6, 0x6b77, 0x8f62, 0x5e74, 0x6190, 0x6200, 0x649a, 0x6f23, 0x7149, 0x7489, 0x79ca, 0x7df4, 0x806f, 0x8f26, 0x84ee, 0x9023, 0x934a, 0x5217, 0x52a3, 0x54bd, 0x70c8, 0x88c2, 0x8aaa, 0x5ec9, 0x5ff5, 0x637b, 0x6bae, 0x7c3e, 0x7375, 0x4ee4, 0x56f9, 0x5be7, 0x5dba, 0x601c, 0x73b2, 0x7469, 0x7f9a, 0x8046, 0x9234, 0x96f6, 0x9748, 0x9818, 0x4f8b, 0x79ae, 0x91b4, 0x96b8, 0x60e1, 0x4e86, 0x50da, 0x5bee, 0x5c3f, 0x6599, 0x6a02, }; static const unsigned short gNormalizeTablef9c0[] = { /* U+f9c0 */ 0x71ce, 0x7642, 0x84fc, 0x907c, 0x9f8d, 0x6688, 0x962e, 0x5289, 0x677b, 0x67f3, 0x6d41, 0x6e9c, 0x7409, 0x7559, 0x786b, 0x7d10, 0x985e, 0x516d, 0x622e, 0x9678, 0x502b, 0x5d19, 0x6dea, 0x8f2a, 0x5f8b, 0x6144, 0x6817, 0x7387, 0x9686, 0x5229, 0x540f, 0x5c65, 0x6613, 0x674e, 0x68a8, 0x6ce5, 0x7406, 0x75e2, 0x7f79, 0x88cf, 0x88e1, 0x91cc, 0x96e2, 0x533f, 0x6eba, 0x541d, 0x71d0, 0x7498, 0x85fa, 0x96a3, 0x9c57, 0x9e9f, 0x6797, 0x6dcb, 0x81e8, 0x7acb, 0x7b20, 0x7c92, 0x72c0, 0x7099, 0x8b58, 0x4ec0, 0x8336, 0x523a, }; static const unsigned short gNormalizeTablefa00[] = { /* U+fa00 */ 0x5207, 0x5ea6, 0x62d3, 0x7cd6, 0x5b85, 0x6d1e, 0x66b4, 0x8f3b, 0x884c, 0x964d, 0x898b, 0x5ed3, 0x5140, 0x55c0, 0xfa0e, 0xfa0f, 0x585a, 0xfa11, 0x6674, 0xfa13, 0xfa14, 0x51de, 0x732a, 0x76ca, 0x793c, 0x795e, 0x7965, 0x798f, 0x9756, 0x7cbe, 0x7fbd, 0xfa1f, 0x8612, 0xfa21, 0x8af8, 0xfa23, 0xfa24, 0x9038, 0x90fd, 0xfa27, 0xfa28, 0xfa29, 0x98ef, 0x98fc, 0x9928, 0x9db4, 0xfa2e, 0xfa2f, 0x4fae, 0x50e7, 0x514d, 0x52c9, 0x52e4, 0x5351, 0x559d, 0x5606, 0x5668, 0x5840, 0x58a8, 0x5c64, 0x5c6e, 0x6094, 0x6168, 0x618e, }; static const unsigned short gNormalizeTablefa40[] = { /* U+fa40 */ 0x61f2, 0x654f, 0x65e2, 0x6691, 0x6885, 0x6d77, 0x6e1a, 0x6f22, 0x716e, 0x722b, 0x7422, 0x7891, 0x793e, 0x7949, 0x7948, 0x7950, 0x7956, 0x795d, 0x798d, 0x798e, 0x7a40, 0x7a81, 0x7bc0, 0x7df4, 0x7e09, 0x7e41, 0x7f72, 0x8005, 0x81ed, 0x8279, 0x8279, 0x8457, 0x8910, 0x8996, 0x8b01, 0x8b39, 0x8cd3, 0x8d08, 0x8fb6, 0x9038, 0x96e3, 0x97ff, 0x983b, 0x6075, 0xfa6c, 0x8218, 0xfa6e, 0xfa6f, 0x4e26, 0x51b5, 0x5168, 0x4f80, 0x5145, 0x5180, 0x52c7, 0x52fa, 0x559d, 0x5555, 0x5599, 0x55e2, 0x585a, 0x58b3, 0x5944, 0x5954, }; static const unsigned short gNormalizeTablefa80[] = { /* U+fa80 */ 0x5a62, 0x5b28, 0x5ed2, 0x5ed9, 0x5f69, 0x5fad, 0x60d8, 0x614e, 0x6108, 0x618e, 0x6160, 0x61f2, 0x6234, 0x63c4, 0x641c, 0x6452, 0x6556, 0x6674, 0x6717, 0x671b, 0x6756, 0x6b79, 0x6bba, 0x6d41, 0x6edb, 0x6ecb, 0x6f22, 0x701e, 0x716e, 0x77a7, 0x7235, 0x72af, 0x732a, 0x7471, 0x7506, 0x753b, 0x761d, 0x761f, 0x76ca, 0x76db, 0x76f4, 0x774a, 0x7740, 0x78cc, 0x7ab1, 0x7bc0, 0x7c7b, 0x7d5b, 0x7df4, 0x7f3e, 0x8005, 0x8352, 0x83ef, 0x8779, 0x8941, 0x8986, 0x8996, 0x8abf, 0x8af8, 0x8acb, 0x8b01, 0x8afe, 0x8aed, 0x8b39, }; static const unsigned short gNormalizeTablefac0[] = { /* U+fac0 */ 0x8b8a, 0x8d08, 0x8f38, 0x9072, 0x9199, 0x9276, 0x967c, 0x96e3, 0x9756, 0x97db, 0x97ff, 0x980b, 0x983b, 0x9b12, 0x9f9c, 0xfacf, 0xfad0, 0xfad1, 0x3b9d, 0x4018, 0x4039, 0xfad5, 0xfad6, 0xfad7, 0x9f43, 0x9f8e, 0xfada, 0xfadb, 0xfadc, 0xfadd, 0xfade, 0xfadf, 0xfae0, 0xfae1, 0xfae2, 0xfae3, 0xfae4, 0xfae5, 0xfae6, 0xfae7, 0xfae8, 0xfae9, 0xfaea, 0xfaeb, 0xfaec, 0xfaed, 0xfaee, 0xfaef, 0xfaf0, 0xfaf1, 0xfaf2, 0xfaf3, 0xfaf4, 0xfaf5, 0xfaf6, 0xfaf7, 0xfaf8, 0xfaf9, 0xfafa, 0xfafb, 0xfafc, 0xfafd, 0xfafe, 0xfaff, }; static const unsigned short gNormalizeTablefb00[] = { /* U+fb00 */ 0x0066, 0x0066, 0x0066, 0x0066, 0x0066, 0x0073, 0x0073, 0xfb07, 0xfb08, 0xfb09, 0xfb0a, 0xfb0b, 0xfb0c, 0xfb0d, 0xfb0e, 0xfb0f, 0xfb10, 0xfb11, 0xfb12, 0x0574, 0x0574, 0x0574, 0x057e, 0x0574, 0xfb18, 0xfb19, 0xfb1a, 0xfb1b, 0xfb1c, 0x05d9, 0xfb1e, 0x05f2, 0x05e2, 0x05d0, 0x05d3, 0x05d4, 0x05db, 0x05dc, 0x05dd, 0x05e8, 0x05ea, 0x002b, 0x05e9, 0x05e9, 0x05e9, 0x05e9, 0x05d0, 0x05d0, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0xfb37, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0xfb3d, 0x05de, 0xfb3f, }; static const unsigned short gNormalizeTablefb40[] = { /* U+fb40 */ 0x05e0, 0x05e1, 0xfb42, 0x05e3, 0x05e4, 0xfb45, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x05d5, 0x05d1, 0x05db, 0x05e4, 0x05d0, 0x0671, 0x0671, 0x067b, 0x067b, 0x067b, 0x067b, 0x067e, 0x067e, 0x067e, 0x067e, 0x0680, 0x0680, 0x0680, 0x0680, 0x067a, 0x067a, 0x067a, 0x067a, 0x067f, 0x067f, 0x067f, 0x067f, 0x0679, 0x0679, 0x0679, 0x0679, 0x06a4, 0x06a4, 0x06a4, 0x06a4, 0x06a6, 0x06a6, 0x06a6, 0x06a6, 0x0684, 0x0684, 0x0684, 0x0684, 0x0683, 0x0683, 0x0683, 0x0683, 0x0686, 0x0686, 0x0686, 0x0686, 0x0687, 0x0687, }; static const unsigned short gNormalizeTablefb80[] = { /* U+fb80 */ 0x0687, 0x0687, 0x068d, 0x068d, 0x068c, 0x068c, 0x068e, 0x068e, 0x0688, 0x0688, 0x0698, 0x0698, 0x0691, 0x0691, 0x06a9, 0x06a9, 0x06a9, 0x06a9, 0x06af, 0x06af, 0x06af, 0x06af, 0x06b3, 0x06b3, 0x06b3, 0x06b3, 0x06b1, 0x06b1, 0x06b1, 0x06b1, 0x06ba, 0x06ba, 0x06bb, 0x06bb, 0x06bb, 0x06bb, 0x06d5, 0x06d5, 0x06c1, 0x06c1, 0x06c1, 0x06c1, 0x06be, 0x06be, 0x06be, 0x06be, 0x06d2, 0x06d2, 0x06d2, 0x06d2, 0xfbb2, 0xfbb3, 0xfbb4, 0xfbb5, 0xfbb6, 0xfbb7, 0xfbb8, 0xfbb9, 0xfbba, 0xfbbb, 0xfbbc, 0xfbbd, 0xfbbe, 0xfbbf, }; static const unsigned short gNormalizeTablefbc0[] = { /* U+fbc0 */ 0xfbc0, 0xfbc1, 0xfbc2, 0xfbc3, 0xfbc4, 0xfbc5, 0xfbc6, 0xfbc7, 0xfbc8, 0xfbc9, 0xfbca, 0xfbcb, 0xfbcc, 0xfbcd, 0xfbce, 0xfbcf, 0xfbd0, 0xfbd1, 0xfbd2, 0x06ad, 0x06ad, 0x06ad, 0x06ad, 0x06c7, 0x06c7, 0x06c6, 0x06c6, 0x06c8, 0x06c8, 0x06c7, 0x06cb, 0x06cb, 0x06c5, 0x06c5, 0x06c9, 0x06c9, 0x06d0, 0x06d0, 0x06d0, 0x06d0, 0x0649, 0x0649, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x06cc, 0x06cc, 0x06cc, 0x06cc, }; static const unsigned short gNormalizeTablefc00[] = { /* U+fc00 */ 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x0628, 0x0628, 0x0628, 0x0628, 0x0628, 0x0628, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062b, 0x062b, 0x062b, 0x062b, 0x062c, 0x062c, 0x062d, 0x062d, 0x062e, 0x062e, 0x062e, 0x0633, 0x0633, 0x0633, 0x0633, 0x0635, 0x0635, 0x0636, 0x0636, 0x0636, 0x0636, 0x0637, 0x0637, 0x0638, 0x0639, 0x0639, 0x063a, 0x063a, 0x0641, 0x0641, 0x0641, 0x0641, 0x0641, 0x0641, 0x0642, 0x0642, 0x0642, 0x0642, 0x0643, 0x0643, 0x0643, 0x0643, 0x0643, 0x0643, 0x0643, 0x0643, 0x0644, }; static const unsigned short gNormalizeTablefc40[] = { /* U+fc40 */ 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0645, 0x0645, 0x0645, 0x0645, 0x0645, 0x0645, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x0647, 0x0647, 0x0647, 0x0647, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x0630, 0x0631, 0x0649, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x0628, 0x0628, 0x0628, 0x0628, 0x0628, 0x0628, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062b, 0x062b, 0x062b, 0x062b, 0x062b, 0x062b, 0x0641, 0x0641, 0x0642, 0x0642, }; static const unsigned short gNormalizeTablefc80[] = { /* U+fc80 */ 0x0643, 0x0643, 0x0643, 0x0643, 0x0643, 0x0644, 0x0644, 0x0644, 0x0645, 0x0645, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x0649, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x0628, 0x0628, 0x0628, 0x0628, 0x0628, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062b, 0x062c, 0x062c, 0x062d, 0x062d, 0x062e, 0x062e, 0x0633, 0x0633, 0x0633, 0x0633, 0x0635, 0x0635, 0x0635, 0x0636, 0x0636, 0x0636, 0x0636, 0x0637, 0x0638, 0x0639, 0x0639, 0x063a, 0x063a, 0x0641, 0x0641, }; static const unsigned short gNormalizeTablefcc0[] = { /* U+fcc0 */ 0x0641, 0x0641, 0x0642, 0x0642, 0x0643, 0x0643, 0x0643, 0x0643, 0x0643, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0645, 0x0645, 0x0645, 0x0645, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x0647, 0x0647, 0x0647, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x064a, 0x0628, 0x0628, 0x062a, 0x062a, 0x062b, 0x062b, 0x0633, 0x0633, 0x0634, 0x0634, 0x0643, 0x0643, 0x0644, 0x0646, 0x0646, 0x064a, 0x064a, 0x0640, 0x0640, 0x0640, 0x0637, 0x0637, 0x0639, 0x0639, 0x063a, 0x063a, 0x0633, 0x0633, 0x0634, 0x0634, 0x062d, }; static const unsigned short gNormalizeTablefd00[] = { /* U+fd00 */ 0x062d, 0x062c, 0x062c, 0x062e, 0x062e, 0x0635, 0x0635, 0x0636, 0x0636, 0x0634, 0x0634, 0x0634, 0x0634, 0x0634, 0x0633, 0x0635, 0x0636, 0x0637, 0x0637, 0x0639, 0x0639, 0x063a, 0x063a, 0x0633, 0x0633, 0x0634, 0x0634, 0x062d, 0x062d, 0x062c, 0x062c, 0x062e, 0x062e, 0x0635, 0x0635, 0x0636, 0x0636, 0x0634, 0x0634, 0x0634, 0x0634, 0x0634, 0x0633, 0x0635, 0x0636, 0x0634, 0x0634, 0x0634, 0x0634, 0x0633, 0x0634, 0x0637, 0x0633, 0x0633, 0x0633, 0x0634, 0x0634, 0x0634, 0x0637, 0x0638, 0x0627, 0x0627, 0xfd3e, 0xfd3f, }; static const unsigned short gNormalizeTablefd40[] = { /* U+fd40 */ 0xfd40, 0xfd41, 0xfd42, 0xfd43, 0xfd44, 0xfd45, 0xfd46, 0xfd47, 0xfd48, 0xfd49, 0xfd4a, 0xfd4b, 0xfd4c, 0xfd4d, 0xfd4e, 0xfd4f, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062c, 0x062c, 0x062d, 0x062d, 0x0633, 0x0633, 0x0633, 0x0633, 0x0633, 0x0633, 0x0633, 0x0633, 0x0635, 0x0635, 0x0635, 0x0634, 0x0634, 0x0634, 0x0634, 0x0634, 0x0634, 0x0634, 0x0636, 0x0636, 0x0636, 0x0637, 0x0637, 0x0637, 0x0637, 0x0639, 0x0639, 0x0639, 0x0639, 0x063a, 0x063a, 0x063a, 0x0641, 0x0641, 0x0642, 0x0642, }; static const unsigned short gNormalizeTablefd80[] = { /* U+fd80 */ 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0645, 0x0645, 0x0645, 0x0645, 0x0645, 0x0645, 0x0645, 0xfd90, 0xfd91, 0x0645, 0x0647, 0x0647, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x0646, 0x064a, 0x064a, 0x0628, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062a, 0x062c, 0x062c, 0x062c, 0x0633, 0x0635, 0x0634, 0x0636, 0x0644, 0x0644, 0x064a, 0x064a, 0x064a, 0x0645, 0x0642, 0x0646, 0x0642, 0x0644, 0x0639, 0x0643, 0x0646, 0x0645, 0x0644, 0x0643, 0x0644, 0x0646, 0x062c, 0x062d, }; static const unsigned short gNormalizeTablefdc0[] = { /* U+fdc0 */ 0x0645, 0x0641, 0x0628, 0x0643, 0x0639, 0x0635, 0x0633, 0x0646, 0xfdc8, 0xfdc9, 0xfdca, 0xfdcb, 0xfdcc, 0xfdcd, 0xfdce, 0xfdcf, 0xfdd0, 0xfdd1, 0xfdd2, 0xfdd3, 0xfdd4, 0xfdd5, 0xfdd6, 0xfdd7, 0xfdd8, 0xfdd9, 0xfdda, 0xfddb, 0xfddc, 0xfddd, 0xfdde, 0xfddf, 0xfde0, 0xfde1, 0xfde2, 0xfde3, 0xfde4, 0xfde5, 0xfde6, 0xfde7, 0xfde8, 0xfde9, 0xfdea, 0xfdeb, 0xfdec, 0xfded, 0xfdee, 0xfdef, 0x0635, 0x0642, 0x0627, 0x0627, 0x0645, 0x0635, 0x0631, 0x0639, 0x0648, 0x0635, 0x0635, 0x062c, 0x0631, 0xfdfd, 0xfdfe, 0xfdff, }; static const unsigned short gNormalizeTablefe00[] = { /* U+fe00 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x002c, 0x3001, 0x3002, 0x003a, 0x003b, 0x0021, 0x003f, 0x3016, 0x3017, 0x002e, 0xfe1a, 0xfe1b, 0xfe1c, 0xfe1d, 0xfe1e, 0xfe1f, 0xfe20, 0xfe21, 0xfe22, 0xfe23, 0xfe24, 0xfe25, 0xfe26, 0xfe27, 0xfe28, 0xfe29, 0xfe2a, 0xfe2b, 0xfe2c, 0xfe2d, 0xfe2e, 0xfe2f, 0x002e, 0x2014, 0x2013, 0x005f, 0x005f, 0x0028, 0x0029, 0x007b, 0x007d, 0x3014, 0x3015, 0x3010, 0x3011, 0x300a, 0x300b, 0x3008, }; static const unsigned short gNormalizeTablefe40[] = { /* U+fe40 */ 0x3009, 0x300c, 0x300d, 0x300e, 0x300f, 0xfe45, 0xfe46, 0x005b, 0x005d, 0x0020, 0x0020, 0x0020, 0x0020, 0x005f, 0x005f, 0x005f, 0x002c, 0x3001, 0x002e, 0xfe53, 0x003b, 0x003a, 0x003f, 0x0021, 0x2014, 0x0028, 0x0029, 0x007b, 0x007d, 0x3014, 0x3015, 0x0023, 0x0026, 0x002a, 0x002b, 0x002d, 0x003c, 0x003e, 0x003d, 0xfe67, 0x005c, 0x0024, 0x0025, 0x0040, 0xfe6c, 0xfe6d, 0xfe6e, 0xfe6f, 0x0020, 0x0640, 0x0020, 0xfe73, 0x0020, 0xfe75, 0x0020, 0x0640, 0x0020, 0x0640, 0x0020, 0x0640, 0x0020, 0x0640, 0x0020, 0x0640, }; static const unsigned short gNormalizeTablefe80[] = { /* U+fe80 */ 0x0621, 0x0627, 0x0627, 0x0627, 0x0627, 0x0648, 0x0648, 0x0627, 0x0627, 0x064a, 0x064a, 0x064a, 0x064a, 0x0627, 0x0627, 0x0628, 0x0628, 0x0628, 0x0628, 0x0629, 0x0629, 0x062a, 0x062a, 0x062a, 0x062a, 0x062b, 0x062b, 0x062b, 0x062b, 0x062c, 0x062c, 0x062c, 0x062c, 0x062d, 0x062d, 0x062d, 0x062d, 0x062e, 0x062e, 0x062e, 0x062e, 0x062f, 0x062f, 0x0630, 0x0630, 0x0631, 0x0631, 0x0632, 0x0632, 0x0633, 0x0633, 0x0633, 0x0633, 0x0634, 0x0634, 0x0634, 0x0634, 0x0635, 0x0635, 0x0635, 0x0635, 0x0636, 0x0636, 0x0636, }; static const unsigned short gNormalizeTablefec0[] = { /* U+fec0 */ 0x0636, 0x0637, 0x0637, 0x0637, 0x0637, 0x0638, 0x0638, 0x0638, 0x0638, 0x0639, 0x0639, 0x0639, 0x0639, 0x063a, 0x063a, 0x063a, 0x063a, 0x0641, 0x0641, 0x0641, 0x0641, 0x0642, 0x0642, 0x0642, 0x0642, 0x0643, 0x0643, 0x0643, 0x0643, 0x0644, 0x0644, 0x0644, 0x0644, 0x0645, 0x0645, 0x0645, 0x0645, 0x0646, 0x0646, 0x0646, 0x0646, 0x0647, 0x0647, 0x0647, 0x0647, 0x0648, 0x0648, 0x0649, 0x0649, 0x064a, 0x064a, 0x064a, 0x064a, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0x0644, 0xfefd, 0xfefe, 0x0020, }; static const unsigned short gNormalizeTableff00[] = { /* U+ff00 */ 0xff00, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, }; static const unsigned short gNormalizeTableff40[] = { /* U+ff40 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2985, 0x2986, 0x3002, 0x300c, 0x300d, 0x3001, 0x30fb, 0x30f2, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, 0x30e7, 0x30c3, 0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad, 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb, 0x30bd, }; static const unsigned short gNormalizeTableff80[] = { /* U+ff80 */ 0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, 0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de, 0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9, 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, 0x3099, 0x309a, 0x0020, 0x1100, 0x1101, 0x11aa, 0x1102, 0x11ac, 0x11ad, 0x1103, 0x1104, 0x1105, 0x11b0, 0x11b1, 0x11b2, 0x11b3, 0x11b4, 0x11b5, 0x111a, 0x1106, 0x1107, 0x1108, 0x1121, 0x1109, 0x110a, 0x110b, 0x110c, 0x110d, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112, 0xffbf, }; static const unsigned short gNormalizeTableffc0[] = { /* U+ffc0 */ 0xffc0, 0xffc1, 0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0xffc8, 0xffc9, 0x1167, 0x1168, 0x1169, 0x116a, 0x116b, 0x116c, 0xffd0, 0xffd1, 0x116d, 0x116e, 0x116f, 0x1170, 0x1171, 0x1172, 0xffd8, 0xffd9, 0x1173, 0x1174, 0x1175, 0xffdd, 0xffde, 0xffdf, 0x00a2, 0x00a3, 0x00ac, 0x0020, 0x00a6, 0x00a5, 0x20a9, 0xffe7, 0x2502, 0x2190, 0x2191, 0x2192, 0x2193, 0x25a0, 0x25cb, 0xffef, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0xfff9, 0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff, }; static const unsigned short* gNormalizeTable[] = { 0, gNormalizeTable0040, gNormalizeTable0080, gNormalizeTable00c0, gNormalizeTable0100, gNormalizeTable0140, gNormalizeTable0180, gNormalizeTable01c0, gNormalizeTable0200, gNormalizeTable0240, gNormalizeTable0280, gNormalizeTable02c0, 0, gNormalizeTable0340, gNormalizeTable0380, gNormalizeTable03c0, gNormalizeTable0400, gNormalizeTable0440, gNormalizeTable0480, gNormalizeTable04c0, gNormalizeTable0500, gNormalizeTable0540, gNormalizeTable0580, 0, gNormalizeTable0600, gNormalizeTable0640, 0, gNormalizeTable06c0, 0, 0, 0, 0, 0, 0, 0, 0, gNormalizeTable0900, gNormalizeTable0940, 0, gNormalizeTable09c0, gNormalizeTable0a00, gNormalizeTable0a40, 0, 0, 0, gNormalizeTable0b40, gNormalizeTable0b80, gNormalizeTable0bc0, 0, gNormalizeTable0c40, 0, gNormalizeTable0cc0, 0, gNormalizeTable0d40, 0, gNormalizeTable0dc0, gNormalizeTable0e00, 0, gNormalizeTable0e80, gNormalizeTable0ec0, gNormalizeTable0f00, gNormalizeTable0f40, gNormalizeTable0f80, 0, gNormalizeTable1000, 0, gNormalizeTable1080, gNormalizeTable10c0, 0, gNormalizeTable1140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, gNormalizeTable1780, 0, gNormalizeTable1800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, gNormalizeTable1b00, gNormalizeTable1b40, 0, 0, 0, 0, 0, 0, gNormalizeTable1d00, gNormalizeTable1d40, gNormalizeTable1d80, 0, gNormalizeTable1e00, gNormalizeTable1e40, gNormalizeTable1e80, gNormalizeTable1ec0, gNormalizeTable1f00, gNormalizeTable1f40, gNormalizeTable1f80, gNormalizeTable1fc0, gNormalizeTable2000, gNormalizeTable2040, gNormalizeTable2080, 0, gNormalizeTable2100, gNormalizeTable2140, gNormalizeTable2180, gNormalizeTable21c0, gNormalizeTable2200, gNormalizeTable2240, gNormalizeTable2280, gNormalizeTable22c0, gNormalizeTable2300, 0, 0, 0, 0, gNormalizeTable2440, gNormalizeTable2480, gNormalizeTable24c0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, gNormalizeTable2a00, gNormalizeTable2a40, 0, gNormalizeTable2ac0, 0, 0, 0, 0, gNormalizeTable2c00, gNormalizeTable2c40, gNormalizeTable2c80, gNormalizeTable2cc0, 0, gNormalizeTable2d40, 0, 0, 0, 0, gNormalizeTable2e80, gNormalizeTable2ec0, gNormalizeTable2f00, gNormalizeTable2f40, gNormalizeTable2f80, gNormalizeTable2fc0, gNormalizeTable3000, gNormalizeTable3040, gNormalizeTable3080, gNormalizeTable30c0, gNormalizeTable3100, gNormalizeTable3140, gNormalizeTable3180, 0, gNormalizeTable3200, gNormalizeTable3240, gNormalizeTable3280, gNormalizeTable32c0, gNormalizeTable3300, gNormalizeTable3340, gNormalizeTable3380, gNormalizeTable33c0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, gNormalizeTablea640, gNormalizeTablea680, 0, gNormalizeTablea700, gNormalizeTablea740, gNormalizeTablea780, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, gNormalizeTablef900, gNormalizeTablef940, gNormalizeTablef980, gNormalizeTablef9c0, gNormalizeTablefa00, gNormalizeTablefa40, gNormalizeTablefa80, gNormalizeTablefac0, gNormalizeTablefb00, gNormalizeTablefb40, gNormalizeTablefb80, gNormalizeTablefbc0, gNormalizeTablefc00, gNormalizeTablefc40, gNormalizeTablefc80, gNormalizeTablefcc0, gNormalizeTablefd00, gNormalizeTablefd40, gNormalizeTablefd80, gNormalizeTablefdc0, gNormalizeTablefe00, gNormalizeTablefe40, gNormalizeTablefe80, gNormalizeTablefec0, gNormalizeTableff00, gNormalizeTableff40, gNormalizeTableff80, gNormalizeTableffc0, }; unsigned int normalize_character(const unsigned int c) { if (c >= 0x10000 || !gNormalizeTable[c >> 6]) return c; return gNormalizeTable[c >> 6][c & 0x3f]; } mediascanner2-0.100+14.04.20140403/src/mediascanner/mozilla/fts3_porter.c0000644000015301777760000011747412317230414026140 0ustar pbusernogroup00000000000000/* ** 2006 September 30 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Implementation of the full-text-search tokenizer that implements ** a Porter stemmer. ** */ /* * This file is based on the SQLite FTS3 Porter Stemmer implementation. * * This is an attempt to provide some level of full-text search to users of * Thunderbird who use languages that are not space/punctuation delimited. * This is accomplished by performing bi-gram indexing of characters fall * into the unicode space occupied by character sets used in such languages. * * Bi-gram indexing means that given the string "12345" we would index the * pairs "12", "23", "34", and "45" (with position information). We do this * because we are not sure where the word/semantic boundaries are in that * string. Then, when a user searches for "234" the FTS3 engine tokenizes the * search query into "23" and "34". Using special phrase-logic FTS3 requires * the matches to have the tokens "23" and "34" adjacent to each other and in * that order. In theory if the user searched for "2345" we we could just * search for "23 NEAR/2 34". Unfortunately, NEAR does not imply ordering, * so even though that would be more efficient, we would lose correctness * and cannot do it. * * The efficiency and usability of bi-gram search assumes that the character * space is large enough and actually observed bi-grams sufficiently * distributed throughout the potential space so that the search bi-grams * generated when the user issues a query find a 'reasonable' number of * documents for each bi-gram match. * * Mozilla contributors: * Makoto Kato * Andrew Sutherland */ /* ** The code in this file is only compiled if: ** ** * The FTS3 module is being built as an extension ** (in which case SQLITE_CORE is not defined), or ** ** * The FTS3 module is being built into the core of ** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) #include #include #include #include #include #include "fts3_tokenizer.h" /* need some defined to compile without sqlite3 code */ #define sqlite3_malloc malloc #define sqlite3_free free #define sqlite3_realloc realloc static const unsigned char sqlite3Utf8Trans1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, }; typedef unsigned char u8; /** * SQLite helper macro from sqlite3.c (really utf.c) to encode a unicode * character into utf8. * * @param zOut A pointer to the current write position that is updated by * the routine. At entry it should point to one-past the last valid * encoded byte. The same holds true at exit. * @param c The character to encode; this should be an unsigned int. */ #define WRITE_UTF8(zOut, c) { \ if( c<0x0080 ){ \ *zOut++ = (u8)(c&0xff); \ } \ else if( c<0x0800 ){ \ *zOut++ = 0xC0 + (u8)((c>>6) & 0x1F); \ *zOut++ = 0x80 + (u8)(c & 0x3F); \ } \ else if( c<0x10000 ){ \ *zOut++ = 0xE0 + (u8)((c>>12) & 0x0F); \ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ *zOut++ = 0x80 + (u8)(c & 0x3F); \ }else{ \ *zOut++ = 0xf0 + (u8)((c>>18) & 0x07); \ *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ *zOut++ = 0x80 + (u8)(c & 0x3F); \ } \ } /** * Fudge factor to avoid buffer overwrites when WRITE_UTF8 is involved. * * Our normalization table includes entries that may result in a larger * utf-8 encoding. Namely, 023a maps to 2c65. This is a growth from 2 bytes * as utf-8 encoded to 3 bytes. This is currently the only transition possible * because 1-byte encodings are known to stay 1-byte and our normalization * table is 16-bit and so can't generate a 4-byte encoded output. * * For simplicity, we just multiple by 2 which covers the current case and * potential growth for 2-byte to 4-byte growth. We can afford to do this * because we're not talking about a lot of memory here as a rule. */ #define MAX_UTF8_GROWTH_FACTOR 2 /** * Helper from sqlite3.c to read a single UTF8 character. * * The clever bit with multi-byte reading is that you keep going until you find * a byte whose top bits are not '10'. A single-byte UTF8 character will have * '00' or '01', and a multi-byte UTF8 character must start with '11'. * * In the event of illegal UTF-8 this macro may read an arbitrary number of * characters but will never read past zTerm. The resulting character value * of illegal UTF-8 can be anything, although efforts are made to return the * illegal character (0xfffd) for UTF-16 surrogates. * * @param zIn A pointer to the current position that is updated by the routine, * pointing at the start of the next character when the routine returns. * @param zTerm A pointer one past the end of the buffer. * @param c The 'unsigned int' to hold the resulting character value. Do not * use a short or a char. */ #define READ_UTF8(zIn, zTerm, c) { \ c = *(zIn++); \ if( c>=0xc0 ){ \ c = sqlite3Utf8Trans1[c-0xc0]; \ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ c = (c<<6) + (0x3f & *(zIn++)); \ } \ if( c<0x80 \ || (c&0xFFFFF800)==0xD800 \ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } \ } /* end of compatible block to complie codes */ /* ** Class derived from sqlite3_tokenizer */ typedef struct porter_tokenizer { sqlite3_tokenizer base; /* Base class */ } porter_tokenizer; /* ** Class derived from sqlit3_tokenizer_cursor */ typedef struct porter_tokenizer_cursor { sqlite3_tokenizer_cursor base; const char *zInput; /* input we are tokenizing */ int nInput; /* size of the input */ int iOffset; /* current position in zInput */ int iToken; /* index of next token to be returned */ unsigned char *zToken; /* storage for current token */ int nAllocated; /* space allocated to zToken buffer */ /** * Store the offset of the second character in the bi-gram pair that we just * emitted so that we can consider it being the first character in a bi-gram * pair. * The value 0 indicates that there is no previous such character. This is * an acceptable sentinel value because the 0th offset can never be the * offset of the second in a bi-gram pair. * * For example, let us say we are tokenizing a string of 4 CJK characters * represented by the byte-string "11223344" where each repeated digit * indicates 2-bytes of storage used to encode the character in UTF-8. * (It actually takes 3, btw.) Then on the passes to emit each token, * the iOffset and iPrevGigramOffset values at entry will be: * * 1122: iOffset = 0, iPrevBigramOffset = 0 * 2233: iOffset = 4, iPrevBigramOffset = 2 * 3344: iOffset = 6, iPrevBigramOffset = 4 * (nothing will be emitted): iOffset = 8, iPrevBigramOffset = 6 */ int iPrevBigramOffset; /* previous result was bi-gram */ } porter_tokenizer_cursor; /* Forward declaration */ static const sqlite3_tokenizer_module porterTokenizerModule; /* from normalize.c */ extern unsigned int normalize_character(const unsigned int c); /* ** Create a new tokenizer instance. */ static int porterCreate( int argc, const char * const *argv, sqlite3_tokenizer **ppTokenizer ){ porter_tokenizer *t; t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t)); if( t==NULL ) return SQLITE_NOMEM; memset(t, 0, sizeof(*t)); *ppTokenizer = &t->base; return SQLITE_OK; } /* ** Destroy a tokenizer */ static int porterDestroy(sqlite3_tokenizer *pTokenizer){ sqlite3_free(pTokenizer); return SQLITE_OK; } /* ** Prepare to begin tokenizing a particular string. The input ** string to be tokenized is zInput[0..nInput-1]. A cursor ** used to incrementally tokenize this string is returned in ** *ppCursor. */ static int porterOpen( sqlite3_tokenizer *pTokenizer, /* The tokenizer */ const char *zInput, int nInput, /* String to be tokenized */ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ ){ porter_tokenizer_cursor *c; c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); if( c==NULL ) return SQLITE_NOMEM; c->zInput = zInput; if( zInput==0 ){ c->nInput = 0; }else if( nInput<0 ){ c->nInput = (int)strlen(zInput); }else{ c->nInput = nInput; } c->iOffset = 0; /* start tokenizing at the beginning */ c->iToken = 0; c->zToken = NULL; /* no space allocated, yet. */ c->nAllocated = 0; c->iPrevBigramOffset = 0; *ppCursor = &c->base; return SQLITE_OK; } /* ** Close a tokenization cursor previously opened by a call to ** porterOpen() above. */ static int porterClose(sqlite3_tokenizer_cursor *pCursor){ porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; sqlite3_free(c->zToken); sqlite3_free(c); return SQLITE_OK; } /* ** Vowel or consonant */ static const char cType[] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 2, 1 }; /* ** isConsonant() and isVowel() determine if their first character in ** the string they point to is a consonant or a vowel, according ** to Porter ruls. ** ** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'. ** 'Y' is a consonant unless it follows another consonant, ** in which case it is a vowel. ** ** In these routine, the letters are in reverse order. So the 'y' rule ** is that 'y' is a consonant unless it is followed by another ** consonent. */ static int isVowel(const char*); static int isConsonant(const char *z){ int j; char x = *z; if( x==0 ) return 0; assert( x>='a' && x<='z' ); j = cType[x-'a']; if( j<2 ) return j; return z[1]==0 || isVowel(z + 1); } static int isVowel(const char *z){ int j; char x = *z; if( x==0 ) return 0; assert( x>='a' && x<='z' ); j = cType[x-'a']; if( j<2 ) return 1-j; return isConsonant(z + 1); } /* ** Let any sequence of one or more vowels be represented by V and let ** C be sequence of one or more consonants. Then every word can be ** represented as: ** ** [C] (VC){m} [V] ** ** In prose: A word is an optional consonant followed by zero or ** vowel-consonant pairs followed by an optional vowel. "m" is the ** number of vowel consonant pairs. This routine computes the value ** of m for the first i bytes of a word. ** ** Return true if the m-value for z is 1 or more. In other words, ** return true if z contains at least one vowel that is followed ** by a consonant. ** ** In this routine z[] is in reverse order. So we are really looking ** for an instance of of a consonant followed by a vowel. */ static int m_gt_0(const char *z){ while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } return *z!=0; } /* Like mgt0 above except we are looking for a value of m which is ** exactly 1 */ static int m_eq_1(const char *z){ while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } if( *z==0 ) return 0; while( isVowel(z) ){ z++; } if( *z==0 ) return 1; while( isConsonant(z) ){ z++; } return *z==0; } /* Like mgt0 above except we are looking for a value of m>1 instead ** or m>0 */ static int m_gt_1(const char *z){ while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } if( *z==0 ) return 0; while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } return *z!=0; } /* ** Return TRUE if there is a vowel anywhere within z[0..n-1] */ static int hasVowel(const char *z){ while( isConsonant(z) ){ z++; } return *z!=0; } /* ** Return TRUE if the word ends in a double consonant. ** ** The text is reversed here. So we are really looking at ** the first two characters of z[]. */ static int doubleConsonant(const char *z){ return isConsonant(z) && z[0]==z[1] && isConsonant(z+1); } /* ** Return TRUE if the word ends with three letters which ** are consonant-vowel-consonent and where the final consonant ** is not 'w', 'x', or 'y'. ** ** The word is reversed here. So we are really checking the ** first three letters and the first one cannot be in [wxy]. */ static int star_oh(const char *z){ return z[0]!=0 && isConsonant(z) && z[0]!='w' && z[0]!='x' && z[0]!='y' && z[1]!=0 && isVowel(z+1) && z[2]!=0 && isConsonant(z+2); } /* ** If the word ends with zFrom and xCond() is true for the stem ** of the word that preceeds the zFrom ending, then change the ** ending to zTo. ** ** The input word *pz and zFrom are both in reverse order. zTo ** is in normal order. ** ** Return TRUE if zFrom matches. Return FALSE if zFrom does not ** match. Not that TRUE is returned even if xCond() fails and ** no substitution occurs. */ static int stem( char **pz, /* The word being stemmed (Reversed) */ const char *zFrom, /* If the ending matches this... (Reversed) */ const char *zTo, /* ... change the ending to this (not reversed) */ int (*xCond)(const char*) /* Condition that must be true */ ){ char *z = *pz; while( *zFrom && *zFrom==*z ){ z++; zFrom++; } if( *zFrom!=0 ) return 0; if( xCond && !xCond(z) ) return 1; while( *zTo ){ *(--z) = *(zTo++); } *pz = z; return 1; } /** * Voiced sound mark is only on Japanese. It is like accent. It combines with * previous character. Example, "サ" (Katakana) with "゛" (voiced sound mark) is * "ザ". Although full-width character mapping has combined character like "ザ", * there is no combined character on half-width Katanaka character mapping. */ static int isVoicedSoundMark(const unsigned int c) { if (c == 0xff9e || c == 0xff9f || c == 0x3099 || c == 0x309a) return 1; return 0; } /** * How many unicode characters to take from the front and back of a term in * |copy_stemmer|. */ #define COPY_STEMMER_COPY_HALF_LEN 10 /** * Normalizing but non-stemming term copying. * * The original function would take 10 bytes from the front and 10 bytes from * the back if there were no digits in the string and it was more than 20 * bytes long. If there were digits involved that would decrease to 3 bytes * from the front and 3 from the back. This would potentially corrupt utf-8 * encoded characters, which is fine from the perspective of the FTS3 logic. * * In our revised form we now operate on a unicode character basis rather than * a byte basis. Additionally we use the same length limit even if there are * digits involved because it's not clear digit token-space reduction is saving * us from anything and could be hurting. Specifically, if no one is ever * going to search on things with digits, then we should just remove them. * Right now, the space reduction is going to increase false positives when * people do search on them and increase the number of collisions sufficiently * to make it really expensive. The caveat is there will be some increase in * index size which could be meaningful if people are receiving lots of emails * full of distinct numbers. * * In order to do the copy-from-the-front and copy-from-the-back trick, once * we reach N characters in, we set zFrontEnd to the current value of zOut * (which represents the termination of the first part of the result string) * and set zBackStart to the value of zOutStart. We then advanced zBackStart * along a character at a time as we write more characters. Once we have * traversed the entire string, if zBackStart > zFrontEnd, then we know * the string should be shrunk using the characters in the two ranges. * * (It would be faster to scan from the back with specialized logic but that * particular logic seems easy to screw up and we don't have unit tests in here * to the extent required.) * * @param zIn Input string to normalize and potentially shrink. * @param nBytesIn The number of bytes in zIn, distinct from the number of * unicode characters encoded in zIn. * @param zOut The string to write our output into. This must have at least * nBytesIn * MAX_UTF8_GROWTH_FACTOR in order to compensate for * normalization that results in a larger utf-8 encoding. * @param pnBytesOut Integer to write the number of bytes in zOut into. */ static void copy_stemmer(const unsigned char *zIn, const int nBytesIn, unsigned char *zOut, int *pnBytesOut){ const unsigned char *zInTerm = zIn + nBytesIn; unsigned char *zOutStart = zOut; unsigned int c; unsigned int charCount = 0; unsigned char *zFrontEnd = NULL, *zBackStart = NULL; unsigned int trashC; /* copy normalized character */ while (zIn < zInTerm) { READ_UTF8(zIn, zInTerm, c); c = normalize_character(c); /* ignore voiced/semi-voiced sound mark */ if (!isVoicedSoundMark(c)) { /* advance one non-voiced sound mark character. */ if (zBackStart) READ_UTF8(zBackStart, zOut, trashC); WRITE_UTF8(zOut, c); charCount++; if (charCount == COPY_STEMMER_COPY_HALF_LEN) { zFrontEnd = zOut; zBackStart = zOutStart; } } } /* if we need to shrink the string, transplant the back bytes */ if (zBackStart > zFrontEnd) { /* this handles when both are null too */ size_t backBytes = zOut - zBackStart; memmove(zFrontEnd, zBackStart, backBytes); zOut = zFrontEnd + backBytes; } *zOut = 0; *pnBytesOut = zOut - zOutStart; } /* ** Stem the input word zIn[0..nIn-1]. Store the output in zOut. ** zOut is at least big enough to hold nIn bytes. Write the actual ** size of the output word (exclusive of the '\0' terminator) into *pnOut. ** ** Any upper-case characters in the US-ASCII character set ([A-Z]) ** are converted to lower case. Upper-case UTF characters are ** unchanged. ** ** Words that are longer than about 20 bytes are stemmed by retaining ** a few bytes from the beginning and the end of the word. If the ** word contains digits, 3 bytes are taken from the beginning and ** 3 bytes from the end. For long words without digits, 10 bytes ** are taken from each end. US-ASCII case folding still applies. ** ** If the input word contains not digits but does characters not ** in [a-zA-Z] then no stemming is attempted and this routine just ** copies the input into the input into the output with US-ASCII ** case folding. ** ** Stemming never increases the length of the word. So there is ** no chance of overflowing the zOut buffer. */ static void porter_stemmer( const unsigned char *zIn, unsigned int nIn, unsigned char *zOut, int *pnOut ){ unsigned int i, j, c; char zReverse[28]; char *z, *z2; const unsigned char *zTerm = zIn + nIn; const unsigned char *zTmp = zIn; if( nIn<3 || nIn>=sizeof(zReverse)-7 ){ /* The word is too big or too small for the porter stemmer. ** Fallback to the copy stemmer */ copy_stemmer(zIn, nIn, zOut, pnOut); return; } for (j = sizeof(zReverse) - 6; zTmp < zTerm; j--) { READ_UTF8(zTmp, zTerm, c); c = normalize_character(c); if( c>='a' && c<='z' ){ zReverse[j] = c; }else{ /* The use of a character not in [a-zA-Z] means that we fallback ** to the copy stemmer */ copy_stemmer(zIn, nIn, zOut, pnOut); return; } } memset(&zReverse[sizeof(zReverse)-5], 0, 5); z = &zReverse[j+1]; /* Step 1a */ if( z[0]=='s' ){ if( !stem(&z, "sess", "ss", 0) && !stem(&z, "sei", "i", 0) && !stem(&z, "ss", "ss", 0) ){ z++; } } /* Step 1b */ z2 = z; if( stem(&z, "dee", "ee", m_gt_0) ){ /* Do nothing. The work was all in the test */ }else if( (stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel)) && z!=z2 ){ if( stem(&z, "ta", "ate", 0) || stem(&z, "lb", "ble", 0) || stem(&z, "zi", "ize", 0) ){ /* Do nothing. The work was all in the test */ }else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){ z++; }else if( m_eq_1(z) && star_oh(z) ){ *(--z) = 'e'; } } /* Step 1c */ if( z[0]=='y' && hasVowel(z+1) ){ z[0] = 'i'; } /* Step 2 */ switch( z[1] ){ case 'a': (void) (stem(&z, "lanoita", "ate", m_gt_0) || stem(&z, "lanoit", "tion", m_gt_0)); break; case 'c': (void) (stem(&z, "icne", "ence", m_gt_0) || stem(&z, "icna", "ance", m_gt_0)); break; case 'e': (void) (stem(&z, "rezi", "ize", m_gt_0)); break; case 'g': (void) (stem(&z, "igol", "log", m_gt_0)); break; case 'l': (void) (stem(&z, "ilb", "ble", m_gt_0) || stem(&z, "illa", "al", m_gt_0) || stem(&z, "iltne", "ent", m_gt_0) || stem(&z, "ile", "e", m_gt_0) || stem(&z, "ilsuo", "ous", m_gt_0)); break; case 'o': (void) (stem(&z, "noitazi", "ize", m_gt_0) || stem(&z, "noita", "ate", m_gt_0) || stem(&z, "rota", "ate", m_gt_0)); break; case 's': (void) (stem(&z, "msila", "al", m_gt_0) || stem(&z, "ssenevi", "ive", m_gt_0) || stem(&z, "ssenluf", "ful", m_gt_0) || stem(&z, "ssensuo", "ous", m_gt_0)); break; case 't': (void) (stem(&z, "itila", "al", m_gt_0) || stem(&z, "itivi", "ive", m_gt_0) || stem(&z, "itilib", "ble", m_gt_0)); break; } /* Step 3 */ switch( z[0] ){ case 'e': (void) (stem(&z, "etaci", "ic", m_gt_0) || stem(&z, "evita", "", m_gt_0) || stem(&z, "ezila", "al", m_gt_0)); break; case 'i': (void) (stem(&z, "itici", "ic", m_gt_0)); break; case 'l': (void) (stem(&z, "laci", "ic", m_gt_0) || stem(&z, "luf", "", m_gt_0)); break; case 's': (void) (stem(&z, "ssen", "", m_gt_0)); break; } /* Step 4 */ switch( z[1] ){ case 'a': if( z[0]=='l' && m_gt_1(z+2) ){ z += 2; } break; case 'c': if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){ z += 4; } break; case 'e': if( z[0]=='r' && m_gt_1(z+2) ){ z += 2; } break; case 'i': if( z[0]=='c' && m_gt_1(z+2) ){ z += 2; } break; case 'l': if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){ z += 4; } break; case 'n': if( z[0]=='t' ){ if( z[2]=='a' ){ if( m_gt_1(z+3) ){ z += 3; } }else if( z[2]=='e' ){ (void) (stem(&z, "tneme", "", m_gt_1) || stem(&z, "tnem", "", m_gt_1) || stem(&z, "tne", "", m_gt_1)); } } break; case 'o': if( z[0]=='u' ){ if( m_gt_1(z+2) ){ z += 2; } }else if( z[3]=='s' || z[3]=='t' ){ (void) (stem(&z, "noi", "", m_gt_1)); } break; case 's': if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ z += 3; } break; case 't': (void) (stem(&z, "eta", "", m_gt_1) || stem(&z, "iti", "", m_gt_1)); break; case 'u': if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ z += 3; } break; case 'v': case 'z': if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){ z += 3; } break; } /* Step 5a */ if( z[0]=='e' ){ if( m_gt_1(z+1) ){ z++; }else if( m_eq_1(z+1) && !star_oh(z+1) ){ z++; } } /* Step 5b */ if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){ z++; } /* z[] is now the stemmed word in reverse order. Flip it back ** around into forward order and return. */ *pnOut = i = strlen(z); zOut[i] = 0; while( *z ){ zOut[--i] = *(z++); } } /** * Indicate whether characters in the 0x30 - 0x7f region can be part of a token. * Letters and numbers can; punctuation (and 'del') can't. */ static const char porterIdChar[] = { /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ }; /** * Test whether a character is a (non-ascii) space character or not. isDelim * uses the existing porter stemmer logic for anything in the ASCII (< 0x80) * space which covers 0x20. * * 0x2000-0x206F is the general punctuation table. 0x2000 - 0x200b are spaces. * The spaces 0x2000 - 0x200a are all defined as roughly equivalent to a * standard 0x20 space. 0x200b is a "zero width space" (ZWSP) and not like an * 0x20 space. 0x202f is a narrow no-break space and roughly equivalent to an * 0x20 space. 0x205f is a "medium mathematical space" and defined as roughly * equivalent to an 0x20 space. */ #define IS_UNI_SPACE(x) (((x)>=0x2000&&(x)<=0x200a) || (x)==0x202f || (x)==0x205f) /** * What we are checking for: * - 0x3001: Ideographic comma (-> 0x2c ',') * - 0x3002: Ideographic full stop (-> 0x2e '.') * - 0xff0c: fullwidth comma (~ wide 0x2c ',') * - 0xff0e: fullwidth full stop (~ wide 0x2e '.') * - 0xff61: halfwidth ideographic full stop (~ narrow 0x3002) * - 0xff64: halfwidth ideographic comma (~ narrow 0x3001) * * It is possible we should be treating other things as delimiters! */ #define IS_JA_DELIM(x) (((x)==0x3001)||((x)==0xFF64)||((x)==0xFF0E)||((x)==0x3002)||((x)==0xFF61)||((x)==0xFF0C)) /** * The previous character was a delimeter (which includes the start of the * string). */ #define BIGRAM_RESET 0 /** * The previous character was a CJK character and we have only seen one of them. * If we had seen more than one in a row it would be the BIGRAM_USE state. */ #define BIGRAM_UNKNOWN 1 /** * We have seen two or more CJK characters in a row. */ #define BIGRAM_USE 2 /** * The previous character was ASCII or something in the unicode general scripts * area that we do not believe is a delimeter. We call it 'alpha' as in * alphabetic/alphanumeric and something that should be tokenized based on * delimiters rather than on a bi-gram basis. */ #define BIGRAM_ALPHA 3 static int isDelim( const unsigned char *zCur, /* IN: current pointer of token */ const unsigned char *zTerm, /* IN: one character beyond end of token */ int *len, /* OUT: analyzed bytes in this token */ int *state /* IN/OUT: analyze state */ ){ const unsigned char *zIn = zCur; unsigned int c; int delim; /* get the unicode character to analyze */ READ_UTF8(zIn, zTerm, c); c = normalize_character(c); *len = zIn - zCur; /* ASCII character range has rule */ if( c < 0x80 ){ // This is original porter stemmer isDelim logic. // 0x0 - 0x1f are all control characters, 0x20 is space, 0x21-0x2f are // punctuation. delim = (c < 0x30 || !porterIdChar[c - 0x30]); // cases: "&a", "&." if (*state == BIGRAM_USE || *state == BIGRAM_UNKNOWN ){ /* previous maybe CJK and current is ascii */ *state = BIGRAM_ALPHA; /*ascii*/ delim = 1; /* must break */ } else if (delim == 1) { // cases: "a.", ".." /* this is delimiter character */ *state = BIGRAM_RESET; /*reset*/ } else { // cases: "aa", ".a" *state = BIGRAM_ALPHA; /*ascii*/ } return delim; } // (at this point we must be a non-ASCII character) /* voiced/semi-voiced sound mark is ignore */ if (isVoicedSoundMark(c) && *state != BIGRAM_ALPHA) { /* ignore this because it is combined with previous char */ return 0; } /* this isn't CJK range, so return as no delim */ // Anything less than 0x2000 (except to U+0E00-U+0EFF and U+1780-U+17FF) // is the general scripts area and should not be bi-gram indexed. // 0xa000 - 0a4cf is the Yi area. It is apparently a phonetic language whose // usage does not appear to have simple delimeter rules, so we're leaving it // as bigram processed. This is a guess, if you know better, let us know. // (We previously bailed on this range too.) // Addition, U+0E00-U+0E7F is Thai, U+0E80-U+0EFF is Laos, // and U+1780-U+17FF is Khmer. It is no easy way to break each word. // So these should use bi-gram too. // cases: "aa", ".a", "&a" if (c < 0xe00 || (c >= 0xf00 && c < 0x1780) || (c >= 0x1800 && c < 0x2000)) { *state = BIGRAM_ALPHA; /* not really ASCII but same idea; tokenize it */ return 0; } // (at this point we must be a bi-grammable char or delimiter) /* this is space character or delim character */ // cases: "a.", "..", "&." if( IS_UNI_SPACE(c) || IS_JA_DELIM(c) ){ *state = BIGRAM_RESET; /* reset */ return 1; /* it actually is a delimiter; report as such */ } // (at this point we must be a bi-grammable char) // cases: "a&" if( *state==BIGRAM_ALPHA ){ /* Previous is ascii and current maybe CJK */ *state = BIGRAM_UNKNOWN; /* mark as unknown */ return 1; /* break to emit the ASCII token*/ } /* We have no rule for CJK!. use bi-gram */ // cases: "&&" if( *state==BIGRAM_UNKNOWN || *state==BIGRAM_USE ){ /* previous state is unknown. mark as bi-gram */ *state = BIGRAM_USE; return 1; /* break to emit the digram */ } // cases: ".&" (*state == BIGRAM_RESET) *state = BIGRAM_UNKNOWN; /* mark as unknown */ return 0; /* no need to break; nothing to emit */ } /** * Generate a new token. There are basically three types of token we can * generate: * - A porter stemmed token. This is a word entirely comprised of ASCII * characters. We run the porter stemmer algorithm against the word. * Because we have no way to know what is and is not an English word * (the only language for which the porter stemmer was designed), this * could theoretically map multiple words that are not variations of the * same word down to the same root, resulting in potentially unexpected * result inclusions in the search results. We accept this result because * there's not a lot we can do about it and false positives are much * better than false negatives. * - A copied token; case/accent-folded but not stemmed. We call the porter * stemmer for all non-CJK cases and it diverts to the copy stemmer if it * sees any non-ASCII characters (after folding) or if the string is too * long. The copy stemmer will shrink the string if it is deemed too long. * - A bi-gram token; two CJK-ish characters. For query reasons we generate a * series of overlapping bi-grams. (We can't require the user to start their * search based on the arbitrary context of the indexed documents.) * * It may be useful to think of this function as operating at the points between * characters. While we are considering the 'current' character (the one after * the 'point'), we are also interested in the 'previous' character (the one * preceding the point). * At any 'point', there are a number of possible situations which I will * illustrate with pairs of characters. 'a' means alphanumeric ASCII or a * non-ASCII character that is not bi-grammable or a delimeter, '.' * means a delimiter (space or punctuation), '&' means a bi-grammable * character. * - aa: We are in the midst of a token. State remains BIGRAM_ALPHA. * - a.: We will generate a porter stemmed or copied token. State was * BIGRAM_ALPHA, gets set to BIGRAM_RESET. * - a&: We will generate a porter stemmed or copied token; we will set our * state to BIGRAM_UNKNOWN to indicate we have seen one bigram character * but that it is not yet time to emit a bigram. * - .a: We are starting a token. State was BIGRAM_RESET, gets set to * BIGRAM_ALPHA. * - ..: We skip/eat the delimeters. State stays BIGRAM_RESET. * - .&: State set to BIGRAM_UNKNOWN to indicate we have seen one bigram char. * - &a: If the state was BIGRAM_USE, we generate a bi-gram token. If the state * was BIGRAM_UNKNOWN we had only seen one CJK character and so don't do * anything. State is set to BIGRAM_ALPHA. * - &.: Same as the "&a" case, but state is set to BIGRAM_RESET. * - &&: We will generate a bi-gram token. State was either BIGRAM_UNKNOWN or * BIGRAM_USE, gets set to BIGRAM_USE. */ static int porterNext( sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */ const char **pzToken, /* OUT: *pzToken is the token text */ int *pnBytes, /* OUT: Number of bytes in token */ int *piStartOffset, /* OUT: Starting offset of token */ int *piEndOffset, /* OUT: Ending offset of token */ int *piPosition /* OUT: Position integer of token */ ){ porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; const unsigned char *z = (unsigned char *) c->zInput; int len = 0; int state; while( c->iOffset < c->nInput ){ int iStartOffset, numChars; /* * This loop basically has two modes of operation: * - general processing (iPrevBigramOffset == 0 here) * - CJK processing (iPrevBigramOffset != 0 here) * * In an general processing pass we skip over all the delimiters, leaving us * at a character that promises to produce a token. This could be a CJK * token (state == BIGRAM_USE) or an ALPHA token (state == BIGRAM_ALPHA). * If it was a CJK token, we transition into CJK state for the next loop. * If it was an alpha token, our current offset is pointing at a delimiter * (which could be a CJK character), so it is good that our next pass * through the function and loop will skip over any delimiters. If the * delimiter we hit was a CJK character, the next time through we will * not treat it as a delimiter though; the entry state for that scan is * BIGRAM_RESET so the transition is not treated as a delimiter! * * The CJK pass always starts with the second character in a bi-gram emitted * as a token in the previous step. No delimiter skipping is required * because we know that first character might produce a token for us. It * only 'might' produce a token because the previous pass performed no * lookahead and cannot be sure it is followed by another CJK character. * This is why */ // If we have a previous bigram offset if (c->iPrevBigramOffset == 0) { /* Scan past delimiter characters */ state = BIGRAM_RESET; /* reset */ while (c->iOffset < c->nInput && isDelim(z + c->iOffset, z + c->nInput, &len, &state)) { c->iOffset += len; } } else { /* for bigram indexing, use previous offset */ c->iOffset = c->iPrevBigramOffset; } /* Count non-delimiter characters. */ iStartOffset = c->iOffset; numChars = 0; // Start from a reset state. This means the first character we see // (which will not be a delimiter) determines which of ALPHA or CJK modes // we are operating in. (It won't be a delimiter because in a 'general' // pass as defined above, we will have eaten all the delimiters, and in // a CJK pass we are guaranteed that the first character is CJK.) state = BIGRAM_RESET; /* state is reset */ // Advance until it is time to emit a token. // For ALPHA characters, this means advancing until we encounter a delimiter // or a CJK character. iOffset will be pointing at the delimiter or CJK // character, aka one beyond the last ALPHA character. // For CJK characters this means advancing until we encounter an ALPHA // character, a delimiter, or we have seen two consecutive CJK // characters. iOffset points at the ALPHA/delimiter in the first 2 cases // and the second of two CJK characters in the last case. // Because of the way this loop is structured, iOffset is only updated // when we don't terminate. However, if we terminate, len still contains // the number of bytes in the character found at iOffset. (This is useful // in the CJK case.) while (c->iOffset < c->nInput && !isDelim(z + c->iOffset, z + c->nInput, &len, &state)) { c->iOffset += len; numChars++; } if (state == BIGRAM_USE) { /* Split word by bigram */ // Right now iOffset is pointing at the second character in a pair. // Save this offset so next-time through we start with that as the // first character. c->iPrevBigramOffset = c->iOffset; // And now advance so that iOffset is pointing at the character after // the second character in the bi-gram pair. Also count the char. c->iOffset += len; numChars++; } else { /* Reset bigram offset */ c->iPrevBigramOffset = 0; } /* We emit a token if: * - there are two ideograms together, * - there are three chars or more, * - we think this is a query and wildcard magic is desired. * We think is a wildcard query when we have at least one * character, our current offset is one shy of nInput and the * character at iOffset is '*'. */ // It is possible we have no token to emit here if iPrevBigramOffset was not // 0 on entry and there was no second CJK character. iPrevBigramOffset // will now be 0 if that is the case (and c->iOffset == iStartOffset). if (// allow two-character words only if in bigram (numChars == 2 && state == BIGRAM_USE) || // otherwise, drop two-letter words (considered stop-words) (numChars >=3) || // wildcard case: (numChars >= 1 && (c->iOffset == c->nInput - 1) && (z[c->iOffset] == '*'))) { /* figure out the number of bytes to copy/stem */ int n = c->iOffset - iStartOffset; /* make sure there is enough buffer space */ if (n * MAX_UTF8_GROWTH_FACTOR > c->nAllocated) { c->nAllocated = n * MAX_UTF8_GROWTH_FACTOR + 20; c->zToken = sqlite3_realloc(c->zToken, c->nAllocated); if (c->zToken == NULL) return SQLITE_NOMEM; } if (state == BIGRAM_USE) { /* This is by bigram. So it is unnecessary to convert word */ copy_stemmer(&z[iStartOffset], n, c->zToken, pnBytes); } else { porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes); } *pzToken = c->zToken; *piStartOffset = iStartOffset; *piEndOffset = c->iOffset; *piPosition = c->iToken++; return SQLITE_OK; } } return SQLITE_DONE; } /* ** The set of routines that implement the porter-stemmer tokenizer */ static const sqlite3_tokenizer_module porterTokenizerModule = { 0, porterCreate, porterDestroy, porterOpen, porterClose, porterNext, }; /* ** Allocate a new porter tokenizer. Return a pointer to the new ** tokenizer in *ppModule */ void sqlite3Fts3PorterTokenizerModule( sqlite3_tokenizer_module const**ppModule ){ *ppModule = &porterTokenizerModule; } #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ mediascanner2-0.100+14.04.20140403/src/mediascanner/mozilla/fts3_tokenizer.h0000644000015301777760000001361712317230414026636 0ustar pbusernogroup00000000000000/* ** 2006 July 10 ** ** The author disclaims copyright to this source code. ** ************************************************************************* ** Defines the interface to tokenizers used by fulltext-search. There ** are three basic components: ** ** sqlite3_tokenizer_module is a singleton defining the tokenizer ** interface functions. This is essentially the class structure for ** tokenizers. ** ** sqlite3_tokenizer is used to define a particular tokenizer, perhaps ** including customization information defined at creation time. ** ** sqlite3_tokenizer_cursor is generated by a tokenizer to generate ** tokens from a particular input. */ #ifndef _FTS3_TOKENIZER_H_ #define _FTS3_TOKENIZER_H_ /* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time. ** If tokenizers are to be allowed to call sqlite3_*() functions, then ** we will need a way to register the API consistently. */ #include "sqlite3.h" /* ** Structures used by the tokenizer interface. When a new tokenizer ** implementation is registered, the caller provides a pointer to ** an sqlite3_tokenizer_module containing pointers to the callback ** functions that make up an implementation. ** ** When an fts3 table is created, it passes any arguments passed to ** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the ** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer ** implementation. The xCreate() function in turn returns an ** sqlite3_tokenizer structure representing the specific tokenizer to ** be used for the fts3 table (customized by the tokenizer clause arguments). ** ** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen() ** method is called. It returns an sqlite3_tokenizer_cursor object ** that may be used to tokenize a specific input buffer based on ** the tokenization rules supplied by a specific sqlite3_tokenizer ** object. */ typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module; typedef struct sqlite3_tokenizer sqlite3_tokenizer; typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor; struct sqlite3_tokenizer_module { /* ** Structure version. Should always be set to 0. */ int iVersion; /* ** Create a new tokenizer. The values in the argv[] array are the ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL ** TABLE statement that created the fts3 table. For example, if ** the following SQL is executed: ** ** CREATE .. USING fts3( ... , tokenizer arg1 arg2) ** ** then argc is set to 2, and the argv[] array contains pointers ** to the strings "arg1" and "arg2". ** ** This method should return either SQLITE_OK (0), or an SQLite error ** code. If SQLITE_OK is returned, then *ppTokenizer should be set ** to point at the newly created tokenizer structure. The generic ** sqlite3_tokenizer.pModule variable should not be initialised by ** this callback. The caller will do so. */ int (*xCreate)( int argc, /* Size of argv array */ const char *const*argv, /* Tokenizer argument strings */ sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */ ); /* ** Destroy an existing tokenizer. The fts3 module calls this method ** exactly once for each successful call to xCreate(). */ int (*xDestroy)(sqlite3_tokenizer *pTokenizer); /* ** Create a tokenizer cursor to tokenize an input buffer. The caller ** is responsible for ensuring that the input buffer remains valid ** until the cursor is closed (using the xClose() method). */ int (*xOpen)( sqlite3_tokenizer *pTokenizer, /* Tokenizer object */ const char *pInput, int nBytes, /* Input buffer */ sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */ ); /* ** Destroy an existing tokenizer cursor. The fts3 module calls this ** method exactly once for each successful call to xOpen(). */ int (*xClose)(sqlite3_tokenizer_cursor *pCursor); /* ** Retrieve the next token from the tokenizer cursor pCursor. This ** method should either return SQLITE_OK and set the values of the ** "OUT" variables identified below, or SQLITE_DONE to indicate that ** the end of the buffer has been reached, or an SQLite error code. ** ** *ppToken should be set to point at a buffer containing the ** normalized version of the token (i.e. after any case-folding and/or ** stemming has been performed). *pnBytes should be set to the length ** of this buffer in bytes. The input text that generated the token is ** identified by the byte offsets returned in *piStartOffset and ** *piEndOffset. *piStartOffset should be set to the index of the first ** byte of the token in the input buffer. *piEndOffset should be set ** to the index of the first byte just past the end of the token in ** the input buffer. ** ** The buffer *ppToken is set to point at is managed by the tokenizer ** implementation. It is only required to be valid until the next call ** to xNext() or xClose(). */ /* TODO(shess) current implementation requires pInput to be ** nul-terminated. This should either be fixed, or pInput/nBytes ** should be converted to zInput. */ int (*xNext)( sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */ const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */ int *piStartOffset, /* OUT: Byte offset of token in input buffer */ int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */ int *piPosition /* OUT: Number of tokens returned before this one */ ); }; struct sqlite3_tokenizer { const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */ /* Tokenizer implementations will typically add additional fields */ }; struct sqlite3_tokenizer_cursor { sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */ /* Tokenizer implementations will typically add additional fields */ }; #endif /* _FTS3_TOKENIZER_H_ */ mediascanner2-0.100+14.04.20140403/src/mediascanner/mozilla/README.mozilla0000644000015301777760000000031212317230414026026 0ustar pbusernogroup00000000000000fts3_porter.c code is from SQLite3. This customized tokenizer "mozporter" by Mozilla supports CJK indexing using bi-gram. So you have to use bi-gram search string if you wanto to search CJK character. mediascanner2-0.100+14.04.20140403/src/mediascanner/MediaStore.hh0000644000015301777760000000437412317230414024422 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASTORE_HH_ #define MEDIASTORE_HH_ #include"scannercore.hh" #include #include namespace mediascanner { struct MediaStorePrivate; class MediaFile; class Album; enum OpenType { MS_READ_ONLY, MS_READ_WRITE }; class MediaStore final { private: MediaStorePrivate *p; public: MediaStore(OpenType access, const std::string &retireprefix=""); MediaStore(const std::string &filename, OpenType access, const std::string &retireprefix=""); MediaStore(const MediaStore &other) = delete; MediaStore operator=(const MediaStore &other) = delete; ~MediaStore(); void insert(const MediaFile &m) const; void remove(const std::string &fname) const; MediaFile lookup(const std::string &filename) const; std::vector query(const std::string &q, MediaType type, int limit=-1) const; std::vector queryAlbums(const std::string &core_term, int limit=-1) const; std::vector getAlbumSongs(const Album& album) const; std::string getETag(const std::string &filename) const; std::vector listSongs(const std::string& artist="", const std::string& album="", const std::string& album_artist="", int limit=-1) const; std::vector listAlbums(const std::string& artist="", const std::string& album_artist="", int limit=-1) const; std::vector listArtists(bool album_artists, int limit=-1); size_t size() const; void pruneDeleted(); void archiveItems(const std::string &prefix); void restoreItems(const std::string &prefix); }; } #endif mediascanner2-0.100+14.04.20140403/src/mediascanner/Album.cc0000644000015301777760000000234012317230414023403 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "Album.hh" using namespace std; namespace mediascanner { Album::Album(const std::string &title, const std::string &artist) : title(title), artist(artist) { } const std::string& Album::getTitle() const noexcept { return title; } const std::string& Album::getArtist() const noexcept { return artist; } bool Album::operator==(const Album &other) const { return title == other.title && artist == other.artist; } bool Album::operator!=(const Album &other) const { return !(*this == other); } } mediascanner2-0.100+14.04.20140403/src/mediascanner/scannercore.hh0000644000015301777760000000156412317230414024666 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SCANNERCORE_H #define SCANNERCORE_H namespace mediascanner { enum MediaType { UnknownMedia, AudioMedia, VideoMedia, AllMedia, }; } #endif mediascanner2-0.100+14.04.20140403/src/mediascanner/MediaStore.cc0000644000015301777760000004770712317230414024417 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "mozilla/fts3_tokenizer.h" #include"MediaStore.hh" #include"MediaFile.hh" #include "Album.hh" #include "internal/sqliteutils.hh" #include "internal/utils.hh" using namespace std; namespace mediascanner { // Increment this whenever changing db schema. // It will cause dbstore to rebuild its tables. static const int schemaVersion = 4; struct MediaStorePrivate { sqlite3 *db; // https://www.sqlite.org/cvstrac/wiki?p=DatabaseIsLocked // http://sqlite.com/faq.html#q6 std::mutex dbMutex; void insert(const MediaFile &m) const; void remove(const std::string &fname) const; MediaFile lookup(const std::string &filename) const; std::vector query(const std::string &q, MediaType type, int limit=-1) const; std::vector queryAlbums(const std::string &core_term, int limit=-1) const; std::vector getAlbumSongs(const Album& album) const; std::string getETag(const std::string &filename) const; size_t size() const; void pruneDeleted(); void archiveItems(const std::string &prefix); void restoreItems(const std::string &prefix); }; extern "C" void sqlite3Fts3PorterTokenizerModule( sqlite3_tokenizer_module const**ppModule); static void register_tokenizer(sqlite3 *db) { Statement query(db, "SELECT fts3_tokenizer(?, ?)"); query.bind(1, "mozporter"); const sqlite3_tokenizer_module *p = NULL; sqlite3Fts3PorterTokenizerModule(&p); query.bind(2, &p, sizeof(p)); query.step(); } /* ranking function adapted from http://sqlite.org/fts3.html#appendix_a */ static void rankfunc(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal) { const int32_t *aMatchinfo; /* Return value of matchinfo() */ int32_t nCol; /* Number of columns in the table */ int32_t nPhrase; /* Number of phrases in the query */ int32_t iPhrase; /* Current phrase */ double score = 0.0; /* Value to return */ /* Check that the number of arguments passed to this function is correct. ** If not, jump to wrong_number_args. Set aMatchinfo to point to the array ** of unsigned integer values returned by FTS function matchinfo. Set ** nPhrase to contain the number of reportable phrases in the users full-text ** query, and nCol to the number of columns in the table. */ if( nVal<1 ) goto wrong_number_args; aMatchinfo = static_cast(sqlite3_value_blob(apVal[0])); nPhrase = aMatchinfo[0]; nCol = aMatchinfo[1]; if( nVal!=(1+nCol) ) goto wrong_number_args; /* Iterate through each phrase in the users query. */ for(iPhrase=0; iPhrase / ) * ** ** aPhraseinfo[] points to the start of the data for phrase iPhrase. So ** the hit count and global hit counts for each column are found in ** aPhraseinfo[iCol*3] and aPhraseinfo[iCol*3+1], respectively. */ const int32_t *aPhraseinfo = &aMatchinfo[2 + iPhrase*nCol*3]; for(iCol=0; iCol0 ){ score += ((double)nHitCount / (double)nGlobalHitCount) * weight; } } } sqlite3_result_double(pCtx, score); return; /* Jump here if the wrong number of arguments are passed to this function */ wrong_number_args: sqlite3_result_error(pCtx, "wrong number of arguments to function rank()", -1); } static void register_functions(sqlite3 *db) { if (sqlite3_create_function(db, "rank", -1, SQLITE_ANY, NULL, rankfunc, NULL, NULL) != SQLITE_OK) { throw runtime_error(sqlite3_errmsg(db)); } } static void execute_sql(sqlite3 *db, const string &cmd) { char *errmsg = nullptr; if(sqlite3_exec(db, cmd.c_str(), nullptr, nullptr, &errmsg) != SQLITE_OK) { throw runtime_error(errmsg); } } static int getSchemaVersion(sqlite3 *db) { int version = -1; try { Statement select(db, "SELECT version FROM schemaVersion"); if (select.step()) version = select.getInt(0); } catch (const exception &e) { /* schemaVersion table might not exist */ } return version; } void deleteTables(sqlite3 *db) { string deleteCmd(R"( DROP TABLE IF EXISTS media; DROP TABLE IF EXISTS media_fts; DROP TABLE IF EXISTS media_attic; DROP TABLE IF EXISTS schemaVersion; )"); execute_sql(db, deleteCmd); } void createTables(sqlite3 *db) { string schema(R"( CREATE TABLE schemaVersion (version INTEGER); CREATE TABLE media ( filename TEXT PRIMARY KEY NOT NULL, content_type TEXT, etag TEXT, title TEXT, date TEXT, artist TEXT, -- Only relevant to audio album TEXT, -- Only relevant to audio album_artist TEXT, -- Only relevant to audio track_number INTEGER, -- Only relevant to audio duration INTEGER, type INTEGER -- 0=Audio, 1=Video ); CREATE INDEX media_album_album_artist_idx ON media(album, album_artist); CREATE TABLE media_attic ( filename TEXT PRIMARY KEY NOT NULL, content_type TEXT, etag TEXT, title TEXT, date TEXT, artist TEXT, -- Only relevant to audio album TEXT, -- Only relevant to audio album_artist TEXT, -- Only relevant to audio track_number INTEGER, -- Only relevant to audio duration INTEGER, type INTEGER -- 0=Audio, 1=Video ); CREATE VIRTUAL TABLE media_fts USING fts4(content='media', title, artist, album, tokenize=mozporter); CREATE TRIGGER media_bu BEFORE UPDATE ON media BEGIN DELETE FROM media_fts WHERE docid=old.rowid; END; CREATE TRIGGER media_au AFTER UPDATE ON media BEGIN INSERT INTO media_fts(docid, title, artist, album) VALUES (new.rowid, new.title, new.artist, new.album); END; CREATE TRIGGER media_bd BEFORE DELETE ON media BEGIN DELETE FROM media_fts WHERE docid=old.rowid; END; CREATE TRIGGER media_ai AFTER INSERT ON media BEGIN INSERT INTO media_fts(docid, title, artist, album) VALUES (new.rowid, new.title, new.artist, new.album); END; )"); execute_sql(db, schema); Statement version(db, "INSERT INTO schemaVersion (version) VALUES (?)"); version.bind(1, schemaVersion); version.step(); } static std::string get_default_database() { std::string cachedir; char *env_cachedir = getenv("MEDIASCANNER_CACHEDIR"); if (env_cachedir) { cachedir = env_cachedir; } else { cachedir = g_get_user_cache_dir(); cachedir += "/mediascanner-2.0"; } if (g_mkdir_with_parents(cachedir.c_str(), S_IRWXU) < 0) { std::string msg("Could not create cache dir: "); msg += strerror(errno); throw runtime_error(msg); } return cachedir + "/mediastore.db"; } MediaStore::MediaStore(OpenType access, const std::string &retireprefix) : MediaStore(get_default_database(), access, retireprefix) { } MediaStore::MediaStore(const std::string &filename, OpenType access, const std::string &retireprefix) { int sqliteFlags = access == MS_READ_WRITE ? SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE : SQLITE_OPEN_READONLY; p = new MediaStorePrivate(); if(sqlite3_open_v2(filename.c_str(), &p->db, sqliteFlags, nullptr) != SQLITE_OK) { throw runtime_error(sqlite3_errmsg(p->db)); } register_tokenizer(p->db); register_functions(p->db); int detectedSchemaVersion = getSchemaVersion(p->db); if(access == MS_READ_WRITE) { if(detectedSchemaVersion != schemaVersion) { deleteTables(p->db); createTables(p->db); } if(!retireprefix.empty()) archiveItems(retireprefix); } else { if(detectedSchemaVersion != schemaVersion) { throw runtime_error("Tried to open a db with an unsupported schema version."); } } } MediaStore::~MediaStore() { sqlite3_close(p->db); delete p; } size_t MediaStorePrivate::size() const { Statement count(db, "SELECT COUNT(*) FROM media"); count.step(); return count.getInt(0); } void MediaStorePrivate::insert(const MediaFile &m) const { Statement query(db, "INSERT OR REPLACE INTO media (filename, content_type, etag, title, date, artist, album, album_artist, track_number, duration, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); string fname = m.getFileName(); string title = m.getTitle(); if(title.empty()) title = filenameToTitle(fname); query.bind(1, fname); query.bind(2, m.getContentType()); query.bind(3, m.getETag()); query.bind(4, title); query.bind(5, m.getDate()); query.bind(6, m.getAuthor()); query.bind(7, m.getAlbum()); string album_artist = m.getAlbumArtist(); if (album_artist.empty()) album_artist = m.getAuthor(); query.bind(8, album_artist); query.bind(9, m.getTrackNumber()); query.bind(10, m.getDuration()); query.bind(11, (int)m.getType()); query.step(); const char *typestr = m.getType() == AudioMedia ? "song" : "video"; printf("Added %s to backing store: %s\n", typestr, m.getFileName().c_str()); printf(" author : '%s'\n", m.getAuthor().c_str()); printf(" title : %s\n", title.c_str()); printf(" album : '%s'\n", m.getAlbum().c_str()); printf(" duration : %d\n", m.getDuration()); } void MediaStorePrivate::remove(const string &fname) const { Statement del(db, "DELETE FROM media WHERE filename = ?"); del.bind(1, fname); del.step(); } static MediaFile make_media(Statement &query) { const string filename = query.getText(0); const string content_type = query.getText(1); const string etag = query.getText(2); const string title = query.getText(3); const string date = query.getText(4); const string author = query.getText(5); const string album = query.getText(6); const string album_artist = query.getText(7); int track_number = query.getInt(8); int duration = query.getInt(9); MediaType type = (MediaType)query.getInt(10); return MediaFile(filename, content_type, etag, title, date, author, album, album_artist, track_number, duration, type); } static vector collect_media(Statement &query) { vector result; while (query.step()) { result.push_back(make_media(query)); } return result; } MediaFile MediaStorePrivate::lookup(const std::string &filename) const { Statement query(db, R"( SELECT filename, content_type, etag, title, date, artist, album, album_artist, track_number, duration, type FROM media WHERE filename = ? )"); query.bind(1, filename); if (!query.step()) { throw runtime_error("Could not find media " + filename); } return make_media(query); } vector MediaStorePrivate::query(const std::string &core_term, MediaType type, int limit) const { if (core_term == "") { Statement query(db, R"( SELECT filename, content_type, etag, title, date, artist, album, album_artist, track_number, duration, type FROM media WHERE type == ? LIMIT ? )"); query.bind(1, (int)type); query.bind(2, limit); return collect_media(query); } else { Statement query(db, R"( SELECT filename, content_type, etag, title, date, artist, album, album_artist, track_number, duration, type FROM media JOIN ( SELECT docid, rank(matchinfo(media_fts), 1.0, 0.5, 0.75) AS rank FROM media_fts WHERE media_fts MATCH ? ) AS ranktable ON (media.rowid = ranktable.docid) WHERE type == ? ORDER BY ranktable.rank DESC LIMIT ? )"); query.bind(1, core_term + "*"); query.bind(2, (int)type); query.bind(3, limit); return collect_media(query); } } static Album make_album(Statement &query) { const string album = query.getText(0); const string album_artist = query.getText(1); return Album(album, album_artist); } static vector collect_albums(Statement &query) { vector result; while (query.step()) { result.push_back(make_album(query)); } return result; } vector MediaStorePrivate::queryAlbums(const std::string &core_term, int limit) const { if (core_term == "") { Statement query(db, R"( SELECT album, album_artist FROM media WHERE type = ? AND album <> '' GROUP BY album, album_artist LIMIT ? )"); query.bind(1, (int)AudioMedia); query.bind(2, limit); return collect_albums(query); } else { Statement query(db, R"( SELECT album, album_artist FROM media WHERE rowid IN (SELECT docid FROM media_fts WHERE media_fts MATCH ?) AND type == ? AND album <> '' GROUP BY album, album_artist LIMIT ? )"); query.bind(1, core_term + "*"); query.bind(2, (int)AudioMedia); query.bind(3, limit); return collect_albums(query); } } vector MediaStorePrivate::getAlbumSongs(const Album& album) const { Statement query(db, R"( SELECT filename, content_type, etag, title, date, artist, album, album_artist, track_number, duration, type FROM media WHERE album = ? AND album_artist = ? AND type = ? ORDER BY track_number )"); query.bind(1, album.getTitle()); query.bind(2, album.getArtist()); query.bind(3, (int)AudioMedia); return collect_media(query); } std::string MediaStorePrivate::getETag(const std::string &filename) const { Statement query(db, R"( SELECT etag FROM media WHERE filename = ? )"); query.bind(1, filename); if (query.step()) { return query.getText(0); } else { return ""; } } std::vector MediaStore::listSongs(const std::string& artist, const std::string& album, const std::string& album_artist, int limit) const { std::string qs(R"( SELECT filename, content_type, etag, title, date, artist, album, album_artist, track_number, duration, type FROM media WHERE type = ? )"); if (!artist.empty()) { qs += " AND artist = ?"; } if (!album.empty()) { qs += " AND album = ?"; } if (!album_artist.empty()) { qs += " AND album_artist = ?"; } qs += R"( ORDER BY album_artist, album, track_number, title LIMIT ? )"; Statement query(p->db, qs.c_str()); int param = 1; query.bind(param++, (int)AudioMedia); if (!artist.empty()) { query.bind(param++, artist); } if (!album.empty()) { query.bind(param++, album); } if (!album_artist.empty()) { query.bind(param++, album_artist); } query.bind(param++, limit); return collect_media(query); } std::vector MediaStore::listAlbums(const std::string& artist, const std::string& album_artist, int limit) const { std::string qs(R"( SELECT album, album_artist FROM media WHERE type = ? )"); if (!artist.empty()) { qs += " AND artist = ?"; } if (!album_artist.empty()) { qs += " AND album_artist = ?"; } qs += R"( GROUP BY album, album_artist ORDER BY album_artist, album LIMIT ? )"; Statement query(p->db, qs.c_str()); int param = 1; query.bind(param++, (int)AudioMedia); if (!artist.empty()) { query.bind(param++, artist); } if (!album_artist.empty()) { query.bind(param++, album_artist); } query.bind(param++, limit); return collect_albums(query); } vector MediaStore::listArtists(bool album_artists, int limit) { const char *qs; if (album_artists) { qs = R"( SELECT album_artist FROM media GROUP BY album_artist ORDER BY album_artist LIMIT ? )"; } else { qs = R"( SELECT artist FROM media GROUP BY artist ORDER BY artist LIMIT ? )"; } Statement query(p->db, qs); query.bind(1, limit); vector artists; while (query.step()) { artists.push_back(query.getText(0)); } return artists; } void MediaStorePrivate::pruneDeleted() { vector deleted; Statement query(db, "SELECT filename FROM media"); while (query.step()) { const string filename = query.getText(0); if (access(filename.c_str(), F_OK) != 0) { deleted.push_back(filename); } } query.finalize(); printf("%d files deleted from disk.\n", (int)deleted.size()); for(const auto &i : deleted) { remove(i); } } void MediaStorePrivate::archiveItems(const std::string &prefix) { const char *templ = R"(BEGIN TRANSACTION; INSERT INTO media_attic SELECT * FROM media WHERE filename LIKE %s; DELETE FROM media WHERE filename LIKE %s; COMMIT; )"; string cond = sqlQuote(prefix + "%"); const size_t bufsize = 1024; char cmd[bufsize]; snprintf(cmd, bufsize, templ, cond.c_str(), cond.c_str()); char *errmsg; if(sqlite3_exec(db, cmd, nullptr, nullptr, &errmsg) != SQLITE_OK) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); throw runtime_error(errmsg); } } void MediaStorePrivate::restoreItems(const std::string &prefix) { const char *templ = R"(BEGIN TRANSACTION; INSERT INTO media SELECT * FROM media_attic WHERE filename LIKE %s; DELETE FROM media_attic WHERE filename LIKE %s; COMMIT; )"; string cond = sqlQuote(prefix + "%"); const size_t bufsize = 1024; char cmd[bufsize]; snprintf(cmd, bufsize, templ, cond.c_str(), cond.c_str()); char *errmsg; if(sqlite3_exec(db, cmd, nullptr, nullptr, &errmsg) != SQLITE_OK) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); throw runtime_error(errmsg); } } void MediaStore::insert(const MediaFile &m) const { std::lock_guard lock(p->dbMutex); p->insert(m); } void MediaStore::remove(const std::string &fname) const { std::lock_guard lock(p->dbMutex); p->remove(fname); } MediaFile MediaStore::lookup(const std::string &filename) const { std::lock_guard lock(p->dbMutex); return p->lookup(filename); } std::vector MediaStore::query(const std::string &q, MediaType type, int limit) const { std::lock_guard lock(p->dbMutex); return p->query(q, type, limit); } std::vector MediaStore::queryAlbums(const std::string &core_term, int limit) const { std::lock_guard lock(p->dbMutex); return p->queryAlbums(core_term, limit); } std::vector MediaStore::getAlbumSongs(const Album& album) const { std::lock_guard lock(p->dbMutex); return p->getAlbumSongs(album); } std::string MediaStore::getETag(const std::string &filename) const { std::lock_guard lock(p->dbMutex); return p->getETag(filename); } size_t MediaStore::size() const { std::lock_guard lock(p->dbMutex); return p->size(); } void MediaStore::pruneDeleted() { std::lock_guard lock(p->dbMutex); p->pruneDeleted(); } void MediaStore::archiveItems(const std::string &prefix) { std::lock_guard lock(p->dbMutex); p->archiveItems(prefix); } void MediaStore::restoreItems(const std::string &prefix) { std::lock_guard lock(p->dbMutex); p->restoreItems(prefix); } } mediascanner2-0.100+14.04.20140403/src/mediascanner/Album.hh0000644000015301777760000000224012317230414023414 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef ALBUM_HH #define ALBUM_HH #include namespace mediascanner { class Album final { public: Album(const std::string &title, const std::string &artist); Album() = delete; const std::string& getTitle() const noexcept; const std::string& getArtist() const noexcept; bool operator==(const Album &other) const; bool operator!=(const Album &other) const; private: std::string title; std::string artist; }; } #endif mediascanner2-0.100+14.04.20140403/src/mediascanner/CMakeLists.txt0000644000015301777760000000115712317230414024601 0ustar pbusernogroup00000000000000add_library(mediascanner SHARED MediaFile.cc MediaFileBuilder.cc Album.cc MediaStore.cc utils.cc mozilla/fts3_porter.c mozilla/Normalize.c ) add_definitions(${MEDIASCANNER_CFLAGS}) target_link_libraries(mediascanner ${MEDIASCANNER_LIBRARIES}) set_target_properties(mediascanner PROPERTIES OUTPUT_NAME "mediascanner-2.0" VERSION 0.0.0 SOVERSION 0 ) install( TARGETS mediascanner LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(FILES Album.hh MediaFile.hh MediaFileBuilder.hh MediaStore.hh scannercore.hh DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/mediascanner-2.0/mediascanner" ) mediascanner2-0.100+14.04.20140403/src/mediascanner/MediaFile.cc0000644000015301777760000000537212317230414024172 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "MediaFile.hh" #include "internal/utils.hh" using namespace std; namespace mediascanner { MediaFile::MediaFile(std::string filename, std::string content_type, std::string etag, std::string title, std::string date, std::string author, std::string album, std::string album_artist, int track_number, int duration, MediaType type) : filename(filename), content_type(content_type), etag(etag), title(title), date(date), author(author), album(album), album_artist(album_artist), track_number(track_number), duration(duration), type(type) { } const std::string& MediaFile::getFileName() const noexcept { return filename; } const std::string& MediaFile::getContentType() const noexcept { return content_type; } const std::string& MediaFile::getETag() const noexcept { return etag; } const std::string& MediaFile::getTitle() const noexcept { return title; } const std::string& MediaFile::getAuthor() const noexcept { return author; } const std::string& MediaFile::getAlbum() const noexcept { return album; } const std::string& MediaFile::getAlbumArtist() const noexcept { return album_artist; } const std::string& MediaFile::getDate() const noexcept { return date; } int MediaFile::getTrackNumber() const noexcept { return track_number; } int MediaFile::getDuration() const noexcept { return duration; } MediaType MediaFile::getType() const noexcept { return type; } std::string MediaFile::getUri() const { return mediascanner::getUri(filename); } bool MediaFile::operator==(const MediaFile &other) const { return filename == other.filename && content_type == other.content_type && etag == other.etag && title == other.title && author == other.author && album == other.album && album_artist == other.album_artist && date == other.date && track_number == other.track_number && duration == other.duration && type == other.type; } bool MediaFile::operator!=(const MediaFile &other) const { return !(*this == other); } } mediascanner2-0.100+14.04.20140403/src/mediascanner/utils.cc0000644000015301777760000000514012317230414023504 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include"internal/utils.hh" #include #include #include namespace mediascanner { std::string sqlQuote(const std::string &input) { std::vector out; out.reserve(input.size() + 2); const char quote = '\''; out.push_back(quote); for(size_t i=0; i std::string filenameToTitle(const std::string &filename) { auto fname_start = filename.rfind('/'); auto suffix_dot = filename.rfind('.'); std::string result; if(fname_start == std::string::npos) { if(suffix_dot == std::string::npos) { result = filename; } else { result = filename.substr(0, suffix_dot); } } else { if(suffix_dot == std::string::npos) { result = filename.substr(fname_start+1, filename.size()); } else { result = filename.substr(fname_start+1, suffix_dot-fname_start-1); } } for(size_t i=0; imessage; g_error_free(error); throw std::runtime_error(msg); } std::string uri(uristr); g_free(uristr); return uri; } } mediascanner2-0.100+14.04.20140403/src/qml/0000755000015301777760000000000012317231021020170 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/0000755000015301777760000000000012317231021021452 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/0000755000015301777760000000000012317231021024003 5ustar pbusernogroup00000000000000mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/SongsModel.cc0000644000015301777760000000430412317230414026372 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "SongsModel.hh" using namespace mediascanner::qml; SongsModel::SongsModel(QObject *parent) : MediaFileModelBase(parent), store(nullptr), artist(""), album(""), album_artist(""), limit(-1) { } MediaStoreWrapper *SongsModel::getStore() { return store; } void SongsModel::setStore(MediaStoreWrapper *store) { if (this->store != store) { this->store = store; update(); } } QString SongsModel::getArtist() { return artist; } void SongsModel::setArtist(const QString artist) { if (this->artist != artist) { this->artist = artist; update(); } } QString SongsModel::getAlbum() { return album; } void SongsModel::setAlbum(const QString album) { if (this->album != album) { this->album = album; update(); } } QString SongsModel::getAlbumArtist() { return album_artist; } void SongsModel::setAlbumArtist(const QString album_artist) { if (this->album_artist != album_artist) { this->album_artist = album_artist; update(); } } int SongsModel::getLimit() { return limit; } void SongsModel::setLimit(int limit) { if (this->limit != limit) { this->limit = limit; update(); } } void SongsModel::update() { if (store == nullptr) { updateResults(std::vector()); } else { updateResults(store->store.listSongs(artist.toStdString(), album.toStdString(), album_artist.toStdString(), limit)); } } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/MediaFileModelBase.hh0000644000015301777760000000337112317230414027730 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_MEDIAFILEMODELBASE_H #define MEDIASCANNER_QML_MEDIAFILEMODELBASE_H #include #include #include namespace mediascanner { namespace qml { class MediaFileModelBase : public QAbstractListModel { Q_OBJECT Q_ENUMS(Roles) public: enum Roles { RoleModelData, RoleFilename, RoleUri, RoleContentType, RoleETag, RoleTitle, RoleAuthor, RoleAlbum, RoleAlbumArtist, RoleDate, RoleTrackNumber, RoleDuration, RoleArt, }; explicit MediaFileModelBase(QObject *parent = 0); int rowCount(const QModelIndex &parent=QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; protected: QHash roleNames() const override; void updateResults(const std::vector &results); private: QHash roles; std::vector results; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/SongsModel.hh0000644000015301777760000000350212317230414026403 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_SONGSMODEL_H #define MEDIASCANNER_QML_SONGSMODEL_H #include #include "MediaStoreWrapper.hh" #include "MediaFileModelBase.hh" namespace mediascanner { namespace qml { class SongsModel : public MediaFileModelBase { Q_OBJECT Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) Q_PROPERTY(QString artist READ getArtist WRITE setArtist) Q_PROPERTY(QString album READ getAlbum WRITE setAlbum) Q_PROPERTY(QString albumArtist READ getAlbumArtist WRITE setAlbumArtist) Q_PROPERTY(int limit READ getLimit WRITE setLimit) public: explicit SongsModel(QObject *parent=0); MediaStoreWrapper *getStore(); void setStore(MediaStoreWrapper *store); QString getArtist(); void setArtist(const QString artist); QString getAlbum(); void setAlbum(const QString album); QString getAlbumArtist(); void setAlbumArtist(const QString album_artist); int getLimit(); void setLimit(int limit); private: void update(); MediaStoreWrapper *store; QString artist, album, album_artist; int limit; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/utils.hh0000644000015301777760000000170112317230414025470 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANER_QML_UTILS_H #define MEDIASCANER_QML_UTILS_H #include #include namespace mediascanner { namespace qml { QString make_album_art_uri(const std::string &artist, const std::string &album); } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/plugin.hh0000644000015301777760000000202312317230414025624 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef PLUGIN_HH_ #define PLUGIN_HH_ #include namespace mediascanner { namespace qml { class MediaScannerPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: virtual void registerTypes(const char *uri) override; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/AlbumModelBase.cc0000644000015301777760000000354412317230414027141 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "AlbumModelBase.hh" #include "utils.hh" using namespace mediascanner::qml; AlbumModelBase::AlbumModelBase(QObject *parent) : QAbstractListModel(parent) { roles[Roles::RoleTitle] = "title"; roles[Roles::RoleArtist] = "artist"; roles[Roles::RoleArt] = "art"; } int AlbumModelBase::rowCount(const QModelIndex &) const { return results.size(); } QVariant AlbumModelBase::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= (ptrdiff_t)results.size()) { return QVariant(); } const mediascanner::Album &album = results[index.row()]; switch (role) { case RoleTitle: return QString::fromStdString(album.getTitle()); case RoleArtist: return QString::fromStdString(album.getArtist()); case RoleArt: return make_album_art_uri(album.getArtist(), album.getTitle()); default: return QVariant(); } } QHash AlbumModelBase::roleNames() const { return roles; } void AlbumModelBase::updateResults(const std::vector &results) { beginResetModel(); this->results = results; endResetModel(); } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.hh0000644000015301777760000000272012317230414027727 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_MEDIASTOREWRAPPER_H #define MEDIASCANNER_QML_MEDIASTOREWRAPPER_H #include #include #include #include #include "MediaFileWrapper.hh" namespace mediascanner { namespace qml { class MediaStoreWrapper : public QObject { Q_OBJECT Q_ENUMS(MediaType) public: enum MediaType { AudioMedia = mediascanner::AudioMedia, VideoMedia = mediascanner::VideoMedia, AllMedia = mediascanner::AllMedia, }; MediaStoreWrapper(QObject *parent=0); Q_INVOKABLE QList query(const QString &q, MediaType type); Q_INVOKABLE mediascanner::qml::MediaFileWrapper *lookup(const QString &filename); mediascanner::MediaStore store; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/AlbumModelBase.hh0000644000015301777760000000300712317230414027145 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_ALBUMMODELBASE_H #define MEDIASCANNER_QML_ALBUMMODELBASE_H #include #include #include namespace mediascanner { namespace qml { class AlbumModelBase : public QAbstractListModel { Q_OBJECT Q_ENUMS(Roles) public: enum Roles { RoleTitle, RoleArtist, RoleArt, }; explicit AlbumModelBase(QObject *parent = 0); int rowCount(const QModelIndex &parent=QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; protected: QHash roleNames() const override; void updateResults(const std::vector &results); private: QHash roles; std::vector results; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/MediaFileWrapper.hh0000644000015301777760000000406712317230414027520 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_MEDIAFILEWRAPPER_H #define MEDIASCANNER_QML_MEDIAFILEWRAPPER_H #include #include #include namespace mediascanner { namespace qml { class MediaFileWrapper : public QObject { Q_OBJECT Q_PROPERTY(QString filename READ filename CONSTANT) Q_PROPERTY(QString uri READ uri CONSTANT) Q_PROPERTY(QString contentType READ contentType CONSTANT) Q_PROPERTY(QString eTag READ eTag CONSTANT) Q_PROPERTY(QString title READ title CONSTANT) Q_PROPERTY(QString author READ author CONSTANT) Q_PROPERTY(QString album READ album CONSTANT) Q_PROPERTY(QString albumArtist READ albumArtist CONSTANT) Q_PROPERTY(QString date READ date CONSTANT) Q_PROPERTY(int trackNumber READ trackNumber CONSTANT) Q_PROPERTY(int duration READ duration CONSTANT) Q_PROPERTY(QString art READ art CONSTANT) public: MediaFileWrapper(const mediascanner::MediaFile &media, QObject *parent=0); QString filename() const; QString uri() const; QString contentType() const; QString eTag() const; QString title() const; QString author() const; QString album() const; QString albumArtist() const; QString date() const; int trackNumber() const; int duration() const; QString art() const; private: const mediascanner::MediaFile media; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/MediaFileWrapper.cc0000644000015301777760000000410312317230414027475 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "MediaFileWrapper.hh" #include "utils.hh" using namespace mediascanner::qml; MediaFileWrapper::MediaFileWrapper(const mediascanner::MediaFile &media, QObject *parent) : QObject(parent), media(media) { } QString MediaFileWrapper::filename() const { return QString::fromStdString(media.getFileName()); } QString MediaFileWrapper::uri() const { return QString::fromStdString(media.getUri()); } QString MediaFileWrapper::contentType() const { return QString::fromStdString(media.getContentType()); } QString MediaFileWrapper::eTag() const { return QString::fromStdString(media.getETag()); } QString MediaFileWrapper::title() const { return QString::fromStdString(media.getTitle()); } QString MediaFileWrapper::author() const { return QString::fromStdString(media.getAuthor()); } QString MediaFileWrapper::album() const { return QString::fromStdString(media.getAlbum()); } QString MediaFileWrapper::albumArtist() const { return QString::fromStdString(media.getAlbumArtist()); } QString MediaFileWrapper::date() const { return QString::fromStdString(media.getDate()); } int MediaFileWrapper::trackNumber() const { return media.getTrackNumber(); } int MediaFileWrapper::duration() const { return media.getDuration(); } QString MediaFileWrapper::art() const { return make_album_art_uri(media.getAuthor(), media.getAlbum()); } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/qmldir0000644000015301777760000000006312317230414025222 0ustar pbusernogroup00000000000000module Ubuntu.MediaScanner plugin mediascanner-qml mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/ArtistsModel.cc0000644000015301777760000000446712317230414026744 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "ArtistsModel.hh" using namespace mediascanner::qml; ArtistsModel::ArtistsModel(QObject *parent) : QAbstractListModel(parent), store(nullptr), album_artists(false), limit(-1) { roles[Roles::RoleArtist] = "artist"; } int ArtistsModel::rowCount(const QModelIndex &) const { return results.size(); } QVariant ArtistsModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= (ptrdiff_t)results.size()) { return QVariant(); } switch (role) { case RoleArtist: return QString::fromStdString(results[index.row()]); default: return QVariant(); } } QHash ArtistsModel::roleNames() const { return roles; } MediaStoreWrapper *ArtistsModel::getStore() { return store; } void ArtistsModel::setStore(MediaStoreWrapper *store) { if (this->store != store) { this->store = store; update(); } } bool ArtistsModel::getAlbumArtists() { return album_artists; } void ArtistsModel::setAlbumArtists(bool album_artists) { if (this->album_artists != album_artists) { this->album_artists = album_artists; update(); } } int ArtistsModel::getLimit() { return limit; } void ArtistsModel::setLimit(int limit) { if (this->limit != limit) { this->limit = limit; update(); } } void ArtistsModel::update() { beginResetModel(); if (store == nullptr) { this->results.clear(); } else { this->results = store->store.listArtists(album_artists, limit); } this->results = results; endResetModel(); } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/ArtistsModel.hh0000644000015301777760000000363712317230414026754 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_ARTISTSMODEL_H #define MEDIASCANNER_QML_ARTISTSMODEL_H #include #include #include #include "MediaStoreWrapper.hh" namespace mediascanner { namespace qml { class ArtistsModel : public QAbstractListModel { Q_OBJECT Q_ENUMS(Roles) Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) Q_PROPERTY(bool albumArtists READ getAlbumArtists WRITE setAlbumArtists) Q_PROPERTY(int limit READ getLimit WRITE setLimit) public: enum Roles { RoleArtist, }; explicit ArtistsModel(QObject *parent = 0); int rowCount(const QModelIndex &parent=QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; protected: QHash roleNames() const override; MediaStoreWrapper *getStore(); void setStore(MediaStoreWrapper *store); bool getAlbumArtists(); void setAlbumArtists(bool album_artists); int getLimit(); void setLimit(int limit); private: void update(); QHash roles; std::vector results; MediaStoreWrapper *store; bool album_artists; int limit; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/AlbumsModel.cc0000644000015301777760000000374412317230414026533 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "AlbumsModel.hh" using namespace mediascanner::qml; AlbumsModel::AlbumsModel(QObject *parent) : AlbumModelBase(parent), store(nullptr), artist(""), album_artist(""), limit(-1) { } MediaStoreWrapper *AlbumsModel::getStore() { return store; } void AlbumsModel::setStore(MediaStoreWrapper *store) { if (this->store != store) { this->store = store; update(); } } QString AlbumsModel::getArtist() { return artist; } void AlbumsModel::setArtist(const QString artist) { if (this->artist != artist) { this->artist = artist; update(); } } QString AlbumsModel::getAlbumArtist() { return album_artist; } void AlbumsModel::setAlbumArtist(const QString album_artist) { if (this->album_artist != album_artist) { this->album_artist = album_artist; update(); } } int AlbumsModel::getLimit() { return limit; } void AlbumsModel::setLimit(int limit) { if (this->limit != limit) { this->limit = limit; update(); } } void AlbumsModel::update() { if (store == nullptr) { updateResults(std::vector()); } else { updateResults(store->store.listAlbums(artist.toStdString(), album_artist.toStdString(), limit)); } } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/AlbumsModel.hh0000644000015301777760000000327412317230414026543 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_ALBUMSMODEL_H #define MEDIASCANNER_QML_ALBUMSMODEL_H #include #include "MediaStoreWrapper.hh" #include "AlbumModelBase.hh" namespace mediascanner { namespace qml { class AlbumsModel : public AlbumModelBase { Q_OBJECT Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) Q_PROPERTY(QString artist READ getArtist WRITE setArtist) Q_PROPERTY(QString albumArtist READ getAlbumArtist WRITE setAlbumArtist) Q_PROPERTY(int limit READ getLimit WRITE setLimit) public: explicit AlbumsModel(QObject *parent=0); MediaStoreWrapper *getStore(); void setStore(MediaStoreWrapper *store); QString getArtist(); void setArtist(const QString artist); QString getAlbumArtist(); void setAlbumArtist(const QString album_artist); int getLimit(); void setLimit(int limit); private: void update(); MediaStoreWrapper *store; QString artist, album_artist; int limit; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/CMakeLists.txt0000644000015301777760000000265712317230414026562 0ustar pbusernogroup00000000000000include_directories(../../..) set(QML_PLUGIN_DIR "${CMAKE_INSTALL_LIBDIR}/qt5/qml/Ubuntu/MediaScanner.0.1") add_library(mediascanner-qml MODULE plugin.cc utils.cc MediaFileWrapper.cc MediaStoreWrapper.cc MediaFileModelBase.cc AlbumModelBase.cc AlbumsModel.cc ArtistsModel.cc SongsModel.cc SongsSearchModel.cc ) set_target_properties(mediascanner-qml PROPERTIES AUTOMOC TRUE) qt5_use_modules(mediascanner-qml Qml) target_link_libraries(mediascanner-qml mediascanner) install( TARGETS mediascanner-qml LIBRARY DESTINATION ${QML_PLUGIN_DIR} ) file(GLOB QMLFILES qmldir ) add_custom_target(mediascanner-qmlfiles ALL COMMAND cp ${QMLFILES} ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${QMLFILES} ) install( FILES ${QMLFILES} DESTINATION ${QML_PLUGIN_DIR} ) if(NOT CMAKE_CROSSCOMPILING) find_program(qmlplugindump_exe qmlplugindump) if(NOT qmlplugindump_exe) msg(FATAL_ERROR "Could not locate qmlplugindump.") endif() # qmlplugindump doesn't run reliably in the CI environment (it seems # to be instantiating the types, which fails when there is no media # database). So add a add_custom_target(update-qmltypes COMMAND QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/src/qml ${qmlplugindump_exe} -notrelocatable Ubuntu.MediaScanner 0.1 > ${CMAKE_CURRENT_SOURCE_DIR}/plugin.qmltypes DEPENDS mediascanner-qml mediascanner-qmlfiles ) endif() install( FILES plugin.qmltypes DESTINATION ${QML_PLUGIN_DIR} ) mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/MediaFileModelBase.cc0000644000015301777760000000614312317230414027716 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "MediaFileModelBase.hh" #include "MediaFileWrapper.hh" #include "utils.hh" using namespace mediascanner::qml; MediaFileModelBase::MediaFileModelBase(QObject *parent) : QAbstractListModel(parent) { roles[Roles::RoleModelData] = "modelData"; roles[Roles::RoleFilename] = "filename"; roles[Roles::RoleUri] = "uri"; roles[Roles::RoleContentType] = "contentType"; roles[Roles::RoleETag] = "eTag"; roles[Roles::RoleTitle] = "title"; roles[Roles::RoleAuthor] = "author"; roles[Roles::RoleAlbum] = "album"; roles[Roles::RoleAlbumArtist] = "albumArtist"; roles[Roles::RoleDate] = "date"; roles[Roles::RoleTrackNumber] = "trackNumber"; roles[Roles::RoleDuration] = "duration"; roles[Roles::RoleArt] = "art"; } int MediaFileModelBase::rowCount(const QModelIndex &) const { return results.size(); } QVariant MediaFileModelBase::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= (ptrdiff_t)results.size()) { return QVariant(); } const mediascanner::MediaFile &media = results[index.row()]; switch (role) { case RoleModelData: return QVariant::fromValue(new MediaFileWrapper(media)); case RoleFilename: return QString::fromStdString(media.getFileName()); case RoleUri: return QString::fromStdString(media.getUri()); case RoleContentType: return QString::fromStdString(media.getContentType()); case RoleETag: return QString::fromStdString(media.getETag()); case RoleTitle: return QString::fromStdString(media.getTitle()); case RoleAuthor: return QString::fromStdString(media.getAuthor()); case RoleAlbum: return QString::fromStdString(media.getAlbum()); case RoleAlbumArtist: return QString::fromStdString(media.getAlbumArtist()); case RoleDate: return QString::fromStdString(media.getDate()); case RoleTrackNumber: return media.getTrackNumber(); case RoleDuration: return media.getDuration(); case RoleArt: return make_album_art_uri(media.getAuthor(), media.getAlbum()); default: return QVariant(); } } QHash MediaFileModelBase::roleNames() const { return roles; } void MediaFileModelBase::updateResults(const std::vector &results) { beginResetModel(); this->results = results; endResetModel(); } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/plugin.cc0000644000015301777760000000265412317230414025624 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "plugin.hh" #include "MediaFileWrapper.hh" #include "MediaStoreWrapper.hh" #include "AlbumsModel.hh" #include "ArtistsModel.hh" #include "SongsModel.hh" #include "SongsSearchModel.hh" using namespace mediascanner::qml; void MediaScannerPlugin::registerTypes(const char *uri) { qmlRegisterType(uri, 0, 1, "MediaStore"); qmlRegisterUncreatableType(uri, 0, 1, "MediaFile", "Use a MediaStore to retrieve MediaFiles"); qmlRegisterType(uri, 0, 1, "AlbumsModel"); qmlRegisterType(uri, 0, 1, "ArtistsModel"); qmlRegisterType(uri, 0, 1, "SongsModel"); qmlRegisterType(uri, 0, 1, "SongsSearchModel"); } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/SongsSearchModel.hh0000644000015301777760000000265312317230414027537 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MEDIASCANNER_QML_SONGSSEARCHMODEL_H #define MEDIASCANNER_QML_SONGSSEARCHMODEL_H #include #include "MediaStoreWrapper.hh" #include "MediaFileModelBase.hh" namespace mediascanner { namespace qml { class SongsSearchModel : public MediaFileModelBase { Q_OBJECT Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) Q_PROPERTY(QString query READ getQuery WRITE setQuery) public: explicit SongsSearchModel(QObject *parent=0); MediaStoreWrapper *getStore(); void setStore(MediaStoreWrapper *store); QString getQuery(); void setQuery(const QString query); private: void update(); MediaStoreWrapper *store; QString query; }; } } #endif mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/plugin.qmltypes0000644000015301777760000001144612317230414027114 0ustar pbusernogroup00000000000000import QtQuick.tooling 1.1 // This file describes the plugin-supplied types contained in the library. // It is used for QML tooling purposes only. // // This file was auto-generated by: // 'qmlplugindump -notrelocatable Ubuntu.MediaScanner 0.1' Module { Component { name: "mediascanner::qml::AlbumModelBase" prototype: "QAbstractListModel" Enum { name: "Roles" values: { "RoleTitle": 0, "RoleArtist": 1, "RoleArt": 2 } } } Component { name: "mediascanner::qml::AlbumsModel" prototype: "mediascanner::qml::AlbumModelBase" exports: ["Ubuntu.MediaScanner/AlbumsModel 0.1"] exportMetaObjectRevisions: [0] Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } Property { name: "artist"; type: "string" } Property { name: "albumArtist"; type: "string" } Property { name: "limit"; type: "int" } } Component { name: "mediascanner::qml::ArtistsModel" prototype: "QAbstractListModel" exports: ["Ubuntu.MediaScanner/ArtistsModel 0.1"] exportMetaObjectRevisions: [0] Enum { name: "Roles" values: { "RoleArtist": 0 } } Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } Property { name: "albumArtists"; type: "bool" } Property { name: "limit"; type: "int" } } Component { name: "mediascanner::qml::MediaFileModelBase" prototype: "QAbstractListModel" Enum { name: "Roles" values: { "RoleModelData": 0, "RoleFilename": 1, "RoleUri": 2, "RoleContentType": 3, "RoleETag": 4, "RoleTitle": 5, "RoleAuthor": 6, "RoleAlbum": 7, "RoleAlbumArtist": 8, "RoleDate": 9, "RoleTrackNumber": 10, "RoleDuration": 11, "RoleArt": 12 } } } Component { name: "mediascanner::qml::MediaFileWrapper" prototype: "QObject" exports: ["Ubuntu.MediaScanner/MediaFile 0.1"] exportMetaObjectRevisions: [0] Property { name: "filename"; type: "string"; isReadonly: true } Property { name: "uri"; type: "string"; isReadonly: true } Property { name: "contentType"; type: "string"; isReadonly: true } Property { name: "eTag"; type: "string"; isReadonly: true } Property { name: "title"; type: "string"; isReadonly: true } Property { name: "author"; type: "string"; isReadonly: true } Property { name: "album"; type: "string"; isReadonly: true } Property { name: "albumArtist"; type: "string"; isReadonly: true } Property { name: "date"; type: "string"; isReadonly: true } Property { name: "trackNumber"; type: "int"; isReadonly: true } Property { name: "duration"; type: "int"; isReadonly: true } Property { name: "art"; type: "string"; isReadonly: true } } Component { name: "mediascanner::qml::MediaStoreWrapper" prototype: "QObject" exports: ["Ubuntu.MediaScanner/MediaStore 0.1"] exportMetaObjectRevisions: [0] Enum { name: "MediaType" values: { "AudioMedia": 1, "VideoMedia": 2, "AllMedia": 3 } } Method { name: "query" type: "QList" Parameter { name: "q"; type: "string" } Parameter { name: "type"; type: "MediaType" } } Method { name: "lookup" type: "mediascanner::qml::MediaFileWrapper*" Parameter { name: "filename"; type: "string" } } } Component { name: "mediascanner::qml::SongsModel" prototype: "mediascanner::qml::MediaFileModelBase" exports: ["Ubuntu.MediaScanner/SongsModel 0.1"] exportMetaObjectRevisions: [0] Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } Property { name: "artist"; type: "string" } Property { name: "album"; type: "string" } Property { name: "albumArtist"; type: "string" } Property { name: "limit"; type: "int" } } Component { name: "mediascanner::qml::SongsSearchModel" prototype: "mediascanner::qml::MediaFileModelBase" exports: ["Ubuntu.MediaScanner/SongsSearchModel 0.1"] exportMetaObjectRevisions: [0] Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } Property { name: "query"; type: "string" } } } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.cc0000644000015301777760000000321512317230414027715 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "MediaStoreWrapper.hh" #include using namespace mediascanner::qml; MediaStoreWrapper::MediaStoreWrapper(QObject *parent) : QObject(parent), store(MS_READ_ONLY) { } QList MediaStoreWrapper::query(const QString &q, MediaType type) { QList result; for (const auto &media : store.query(q.toStdString(), static_cast(type))) { auto wrapper = new MediaFileWrapper(media); QQmlEngine::setObjectOwnership(wrapper, QQmlEngine::JavaScriptOwnership); result.append(wrapper); } return result; } MediaFileWrapper *MediaStoreWrapper::lookup(const QString &filename) { MediaFileWrapper *wrapper; try { wrapper = new MediaFileWrapper(store.lookup(filename.toStdString())); } catch (std::runtime_error &e) { return nullptr; } QQmlEngine::setObjectOwnership(wrapper, QQmlEngine::JavaScriptOwnership); return wrapper; } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/SongsSearchModel.cc0000644000015301777760000000303512317230414027520 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "SongsSearchModel.hh" using namespace mediascanner::qml; SongsSearchModel::SongsSearchModel(QObject *parent) : MediaFileModelBase(parent), store(nullptr), query("") { } MediaStoreWrapper *SongsSearchModel::getStore() { return store; } void SongsSearchModel::setStore(MediaStoreWrapper *store) { if (this->store != store) { this->store = store; update(); } } QString SongsSearchModel::getQuery() { return query; } void SongsSearchModel::setQuery(const QString query) { if (this->query != query) { this->query = query; update(); } } void SongsSearchModel::update() { if (store == nullptr) { updateResults(std::vector()); } else { updateResults(store->store.query(query.toStdString(), mediascanner::AudioMedia)); } } mediascanner2-0.100+14.04.20140403/src/qml/Ubuntu/MediaScanner/utils.cc0000644000015301777760000000214412317230414025460 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical, Ltd. * * Authors: * James Henstridge * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "utils.hh" #include namespace mediascanner { namespace qml { QString make_album_art_uri(const std::string &artist, const std::string &album) { QString result = "image://albumart/artist="; result += QUrl::toPercentEncoding(QString::fromStdString(artist)); result += "&album="; result += QUrl::toPercentEncoding(QString::fromStdString(album)); return result; } } } mediascanner2-0.100+14.04.20140403/COPYING.LGPL0000644000015301777760000001674312317230414020420 0ustar pbusernogroup00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.